From 3154b2aa47ad6bef30aec1a535a2879bc4218c6e Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 27 Sep 2022 14:22:47 +0200 Subject: [PATCH 001/185] Update the risk score documentation links to use the official documentation (#141792) --- .../security_solution/common/constants.ts | 4 ---- .../cti_details/host_risk_summary.tsx | 1 - .../cti_details/user_risk_summary.tsx | 1 - .../host_risk_score_disabled.tsx | 2 +- .../user_risk_score.disabled.tsx | 2 +- .../risk_score_doc_link.tsx | 17 ++--------------- .../risk_score_upgrade_button.tsx | 1 - .../use_risk_score_toast_content.tsx | 2 +- .../components/host_risk_information/index.tsx | 1 - .../public/hosts/components/kpi_hosts/index.tsx | 1 - .../entity_analytics/host_risk_score/index.tsx | 4 ++-- .../entity_analytics/user_risk_score/index.tsx | 4 ++-- .../public/users/components/kpi_users/index.tsx | 1 - .../components/user_risk_information/index.tsx | 1 - 14 files changed, 9 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 967e16d32387..09599d4a289b 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -462,10 +462,6 @@ export enum BulkActionsDryRunErrCode { MACHINE_LEARNING_INDEX_PATTERN = 'MACHINE_LEARNING_INDEX_PATTERN', } -export const RISKY_HOSTS_EXTERNAL_DOC_LINK = - 'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/host-risk-score.md'; -export const RISKY_USERS_EXTERNAL_DOC_LINK = - 'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/user-risk-score.md'; export const RISKY_HOSTS_DOC_LINK = 'https://www.elastic.co/guide/en/security/current/host-risk-score.html'; export const RISKY_USERS_DOC_LINK = diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx index 369122aef39d..68d2d9a65157 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx @@ -41,7 +41,6 @@ const HostRiskSummaryComponent: React.FC<{ values={{ hostRiskScoreDocumentationLink: ( {i18n.ENABLE_HOST_RISK_SCORE_DESCRIPTION}{' '} - + } actions={ diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_disabled/user_risk_score.disabled.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_disabled/user_risk_score.disabled.tsx index 769be20fc362..5649c9698e36 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_disabled/user_risk_score.disabled.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_disabled/user_risk_score.disabled.tsx @@ -38,7 +38,7 @@ const EntityAnalyticsUserRiskScoreDisableComponent = ({ body={ <> {i18n.ENABLE_USER_RISK_SCORE_DESCRIPTION}{' '} - + } actions={ diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx index 78d01879a780..340f63e71bc7 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx @@ -7,35 +7,22 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { - RISKY_HOSTS_DOC_LINK, - RISKY_HOSTS_EXTERNAL_DOC_LINK, - RISKY_USERS_DOC_LINK, - RISKY_USERS_EXTERNAL_DOC_LINK, -} from '../../../../../common/constants'; +import { RISKY_HOSTS_DOC_LINK, RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { LEARN_MORE } from '../../../../overview/components/entity_analytics/host_risk_score/translations'; const RiskScoreDocLinkComponent = ({ - external, riskScoreEntity, title, }: { - external: boolean; riskScoreEntity: RiskScoreEntity; title?: string | React.ReactNode; }) => { - const externalLink = - riskScoreEntity === RiskScoreEntity.user - ? RISKY_USERS_EXTERNAL_DOC_LINK - : RISKY_HOSTS_EXTERNAL_DOC_LINK; - const docLink = riskScoreEntity === RiskScoreEntity.user ? RISKY_USERS_DOC_LINK : RISKY_HOSTS_DOC_LINK; - const link = external ? externalLink : docLink; return ( - + {title ? title : LEARN_MORE} ); diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_upgrade_button.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_upgrade_button.tsx index 6f79950a6504..6ed805850ce2 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_upgrade_button.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_upgrade_button.tsx @@ -94,7 +94,6 @@ const RiskScoreUpgradeButtonComponent = ({ onConfirm={upgradeRiskScore} cancelButtonText={ { const renderDocLink = useCallback( (message: string) => ( <> - {message} + {message} ), [riskScoreEntity] diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx index 9ef78fe893aa..361d982df180 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx @@ -131,7 +131,6 @@ const HostRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => voi values={{ HostRiskScoreDocumentationLink: ( ( <> {i18n.LEARN_MORE}{' '} diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx index 1db3fd6e5d87..794b33c6b33e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx @@ -30,7 +30,7 @@ import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { hostsActions } from '../../../../hosts/store'; import { RiskScoreDonutChart } from '../common/risk_score_donut_chart'; import { BasicTableWithoutBorderBottom } from '../common/basic_table_without_border_bottom'; -import { RISKY_HOSTS_EXTERNAL_DOC_LINK } from '../../../../../common/constants'; +import { RISKY_HOSTS_DOC_LINK } from '../../../../../common/constants'; import { EntityAnalyticsHostRiskScoreDisable } from '../../../../common/components/risk_score/risk_score_disabled/host_risk_score_disabled'; import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; @@ -165,7 +165,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { {i18n.LEARN_MORE} diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx index 35c865243cc2..3b3905d4604a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx @@ -30,7 +30,7 @@ import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirec import { RiskScoreDonutChart } from '../common/risk_score_donut_chart'; import { BasicTableWithoutBorderBottom } from '../common/basic_table_without_border_bottom'; -import { RISKY_USERS_EXTERNAL_DOC_LINK } from '../../../../../common/constants'; +import { RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; import { EntityAnalyticsUserRiskScoreDisable } from '../../../../common/components/risk_score/risk_score_disabled/user_risk_score.disabled'; import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; @@ -165,7 +165,7 @@ const EntityAnalyticsUserRiskScoresComponent = () => { {i18n.LEARN_MORE} diff --git a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx index 4344c52e10d4..831ca2bdc76f 100644 --- a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx @@ -44,7 +44,6 @@ export const UsersKpiComponent = React.memo( <> {i18n.LEARN_MORE}{' '} diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx index b759e8751e7e..4004299b7e45 100644 --- a/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx @@ -108,7 +108,6 @@ const UserRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => voi values={{ UserRiskScoreDocumentationLink: ( Date: Tue, 27 Sep 2022 08:25:00 -0400 Subject: [PATCH 002/185] [ResponseOps][Alerting] Fixing flaky tests in task_management.ts (#141441) * Fixing flaky test * Updating tests * Fixing tests again * Update x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts Co-authored-by: Ying Mao * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * Updating for pr comments * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * Missing lint * Updating check * Adding type * Remove runAt Co-authored-by: Ying Mao Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../task_manager/task_management.ts | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) 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 e857636d8f67..123259cadf0c 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 @@ -54,8 +54,7 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const testHistoryIndex = '.kibana_task_manager_test_result'; - // Failing: See https://github.com/elastic/kibana/issues/141002 - describe.skip('scheduling and running tasks', () => { + describe('scheduling and running tasks', () => { beforeEach(async () => { // clean up before each test return await supertest.delete('/api/sample_tasks').set('kbn-xsrf', 'xxx').expect(200); @@ -646,36 +645,36 @@ export default function ({ getService }: FtrProviderContext) { const historyItem = random(1, 100); const scheduledTask = await scheduleTask({ taskType: 'sampleTask', - schedule: { interval: '30m' }, + schedule: { interval: '1h' }, params: { historyItem }, }); await retry.try(async () => { expect((await historyDocs()).length).to.eql(1); - const tasks = (await currentTasks()).docs; + const task = await currentTask(scheduledTask.id); - expect(getTaskById(tasks, scheduledTask.id).enabled).to.eql(true); + expect(task.enabled).to.eql(true); }); // disable the task await bulkDisable([scheduledTask.id]); await retry.try(async () => { - const tasks = (await currentTasks()).docs; - - expect(getTaskById(tasks, scheduledTask.id).enabled).to.eql(false); + const task = await currentTask(scheduledTask.id); + expect(task.enabled).to.eql(false); }); // re-enable the task await bulkEnable([scheduledTask.id], true); await retry.try(async () => { - const tasks = (await currentTasks()).docs; - - expect(getTaskById(tasks, scheduledTask.id).enabled).to.eql(true); + const task = await currentTask(scheduledTask.id); - // should get a new document even tho original schedule interval was 30m - expect((await historyDocs()).length).to.eql(2); + expect(task.enabled).to.eql(true); + expect(Date.parse(task.scheduledAt)).to.be.greaterThan( + Date.parse(scheduledTask.scheduledAt) + ); + expect(Date.parse(task.runAt)).to.be.greaterThan(Date.parse(scheduledTask.runAt)); }); }); @@ -683,40 +682,33 @@ export default function ({ getService }: FtrProviderContext) { const historyItem = random(1, 100); const scheduledTask = await scheduleTask({ taskType: 'sampleTask', - schedule: { interval: '30m' }, + schedule: { interval: '1h' }, params: { historyItem }, }); await retry.try(async () => { expect((await historyDocs()).length).to.eql(1); - const tasks = (await currentTasks()).docs; - expect(getTaskById(tasks, scheduledTask.id).enabled).to.eql(true); + const task = await currentTask(scheduledTask.id); + expect(task.enabled).to.eql(true); }); // disable the task await bulkDisable([scheduledTask.id]); await retry.try(async () => { - const tasks = (await currentTasks()).docs; - - expect(getTaskById(tasks, scheduledTask.id).enabled).to.eql(false); + const task = await currentTask(scheduledTask.id); + expect(task.enabled).to.eql(false); }); // re-enable the task await bulkEnable([scheduledTask.id], false); await retry.try(async () => { - const tasks = (await currentTasks()).docs; - - const task = getTaskById(tasks, scheduledTask.id); + const task = await currentTask(scheduledTask.id); expect(task.enabled).to.eql(true); - - // task runAt should be set in the future by greater than 20 minutes - // this assumes it takes less than 10 minutes to disable and renable the task - // since the schedule interval is 30 minutes - expect(Date.parse(task.runAt) - Date.now()).to.be.greaterThan(10 * 60 * 1000); + expect(Date.parse(task.scheduledAt)).to.eql(Date.parse(scheduledTask.scheduledAt)); }); }); From f336734196000e37ee60b58056b04202b928a051 Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Tue, 27 Sep 2022 08:33:01 -0400 Subject: [PATCH 003/185] [Security Solution] Update Action history text to be Response actions history (#141805) --- .../security_solution/common/constants.ts | 7 +++--- .../public/app/deep_links/index.ts | 10 ++++----- .../public/app/home/home_navigations.ts | 10 ++++----- .../public/app/translations.ts | 9 +++++--- .../common/components/navigation/types.ts | 2 +- .../__snapshots__/index.test.tsx.snap | 10 ++++----- .../use_navigation_items.tsx | 2 +- .../public/management/common/breadcrumbs.ts | 8 +++++-- .../public/management/common/constants.ts | 2 +- .../endpoint_responder/action_log_button.tsx | 4 ++-- .../components/actions_log_empty_state.tsx | 8 +++---- .../response_actions_log.test.tsx | 2 +- .../response_actions_log.tsx | 4 ++-- .../translations.tsx | 2 +- .../public/management/links.ts | 12 +++++----- .../details/components/actions_menu.test.tsx | 2 +- .../view/hooks/use_endpoint_action_items.tsx | 4 ++-- .../pages/endpoint_hosts/view/index.test.tsx | 2 +- .../pages/endpoint_hosts/view/translations.ts | 4 ++-- .../public/management/pages/index.tsx | 11 ++++++---- .../pages/response_actions/index.tsx | 4 ++-- .../view/response_actions_list_page.test.tsx | 22 ++++++++++--------- .../view/response_actions_list_page.tsx | 4 ++-- .../public/management/types.ts | 2 +- .../translations/translations/fr-FR.json | 3 --- .../translations/translations/ja-JP.json | 3 --- .../translations/translations/zh-CN.json | 3 --- 27 files changed, 80 insertions(+), 76 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 09599d4a289b..7358fbb33b4b 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -113,7 +113,7 @@ export enum SecurityPageName { noPage = '', overview = 'overview', policies = 'policy', - actionHistory = 'action_history', + responseActionsHistory = 'response_actions_history', rules = 'rules', rulesCreate = 'rules-create', sessions = 'sessions', @@ -159,7 +159,7 @@ export const EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/event_filters` as const; export const HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/host_isolation_exceptions` as const; export const BLOCKLIST_PATH = `${MANAGEMENT_PATH}/blocklist` as const; -export const ACTION_HISTORY_PATH = `${MANAGEMENT_PATH}/action_history` as const; +export const RESPONSE_ACTIONS_HISTORY_PATH = `${MANAGEMENT_PATH}/response_actions_history` as const; export const ENTITY_ANALYTICS_PATH = '/entity_analytics' as const; export const APP_OVERVIEW_PATH = `${APP_PATH}${OVERVIEW_PATH}` as const; export const APP_LANDING_PATH = `${APP_PATH}${LANDING_PATH}` as const; @@ -183,7 +183,8 @@ export const APP_EVENT_FILTERS_PATH = `${APP_PATH}${EVENT_FILTERS_PATH}` as cons export const APP_HOST_ISOLATION_EXCEPTIONS_PATH = `${APP_PATH}${HOST_ISOLATION_EXCEPTIONS_PATH}` as const; export const APP_BLOCKLIST_PATH = `${APP_PATH}${BLOCKLIST_PATH}` as const; -export const APP_ACTION_HISTORY_PATH = `${APP_PATH}${ACTION_HISTORY_PATH}` as const; +export const APP_RESPONSE_ACTIONS_HISTORY_PATH = + `${APP_PATH}${RESPONSE_ACTIONS_HISTORY_PATH}` as const; export const APP_ENTITY_ANALYTICS_PATH = `${APP_PATH}${ENTITY_ANALYTICS_PATH}` as const; // cloud logs to exclude from default index pattern diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 99db764285ab..170e06742dcb 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -42,7 +42,7 @@ import { NETWORK, OVERVIEW, POLICIES, - ACTION_HISTORY, + RESPONSE_ACTIONS_HISTORY, ENTITY_ANALYTICS, RULES, TIMELINES, @@ -65,7 +65,7 @@ import { NETWORK_PATH, OVERVIEW_PATH, POLICIES_PATH, - ACTION_HISTORY_PATH, + RESPONSE_ACTIONS_HISTORY_PATH, ENTITY_ANALYTICS_PATH, RULES_CREATE_PATH, RULES_PATH, @@ -511,9 +511,9 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ path: BLOCKLIST_PATH, }, { - id: SecurityPageName.actionHistory, - title: ACTION_HISTORY, - path: ACTION_HISTORY_PATH, + id: SecurityPageName.responseActionsHistory, + title: RESPONSE_ACTIONS_HISTORY, + path: RESPONSE_ACTIONS_HISTORY_PATH, }, { ...getSecuritySolutionLink('benchmarks'), diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts index ae7c15c73a4d..392e89fbbec0 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts @@ -30,7 +30,7 @@ import { APP_USERS_PATH, APP_KUBERNETES_PATH, APP_LANDING_PATH, - APP_ACTION_HISTORY_PATH, + APP_RESPONSE_ACTIONS_HISTORY_PATH, APP_ENTITY_ANALYTICS_PATH, APP_PATH, } from '../../../common/constants'; @@ -162,10 +162,10 @@ export const navTabs: SecurityNav = { disabled: false, urlKey: 'administration', }, - [SecurityPageName.actionHistory]: { - id: SecurityPageName.actionHistory, - name: i18n.ACTION_HISTORY, - href: APP_ACTION_HISTORY_PATH, + [SecurityPageName.responseActionsHistory]: { + id: SecurityPageName.responseActionsHistory, + name: i18n.RESPONSE_ACTIONS_HISTORY, + href: APP_RESPONSE_ACTIONS_HISTORY_PATH, disabled: false, urlKey: 'administration', }, diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts index 154127f469c9..0e74f701eefd 100644 --- a/x-pack/plugins/security_solution/public/app/translations.ts +++ b/x-pack/plugins/security_solution/public/app/translations.ts @@ -120,9 +120,12 @@ export const BLOCKLIST = i18n.translate('xpack.securitySolution.navigation.block defaultMessage: 'Blocklist', }); -export const ACTION_HISTORY = i18n.translate('xpack.securitySolution.navigation.actionHistory', { - defaultMessage: 'Action history', -}); +export const RESPONSE_ACTIONS_HISTORY = i18n.translate( + 'xpack.securitySolution.navigation.responseActionsHistory', + { + defaultMessage: 'Response actions history', + } +); export const CREATE_NEW_RULE = i18n.translate('xpack.securitySolution.navigation.newRuleTitle', { defaultMessage: 'Create new rule', diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts index 84341f532116..ebfae21d5a5e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts @@ -68,7 +68,7 @@ export interface NavTab { } export const securityNavKeys = [ SecurityPageName.alerts, - SecurityPageName.actionHistory, + SecurityPageName.responseActionsHistory, SecurityPageName.blocklist, SecurityPageName.detectionAndResponse, SecurityPageName.case, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap index 613171d5da89..ec824632e1ae 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/__snapshots__/index.test.tsx.snap @@ -251,13 +251,13 @@ Object { "onClick": [Function], }, Object { - "data-href": "securitySolutionUI/action_history", - "data-test-subj": "navigation-action_history", + "data-href": "securitySolutionUI/response_actions_history", + "data-test-subj": "navigation-response_actions_history", "disabled": false, - "href": "securitySolutionUI/action_history", - "id": "action_history", + "href": "securitySolutionUI/response_actions_history", + "id": "response_actions_history", "isSelected": false, - "name": "Action history", + "name": "Response actions history", "onClick": [Function], }, Object { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx index dfef46f35a23..dc15e371ba63 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx @@ -138,7 +138,7 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record) { ? [navTabs[SecurityPageName.hostIsolationExceptions]] : []), navTabs[SecurityPageName.blocklist], - navTabs[SecurityPageName.actionHistory], + navTabs[SecurityPageName.responseActionsHistory], navTabs[SecurityPageName.cloudSecurityPostureBenchmarks], ], }, diff --git a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts index 12dfa0f28208..8bb985df9610 100644 --- a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts +++ b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts @@ -9,7 +9,11 @@ import type { ChromeBreadcrumb } from '@kbn/core/public'; import { AdministrationSubTab } from '../types'; import { ENDPOINTS_TAB, EVENT_FILTERS_TAB, POLICIES_TAB, TRUSTED_APPS_TAB } from './translations'; import type { AdministrationRouteSpyState } from '../../common/utils/route/types'; -import { HOST_ISOLATION_EXCEPTIONS, BLOCKLIST, ACTION_HISTORY } from '../../app/translations'; +import { + HOST_ISOLATION_EXCEPTIONS, + BLOCKLIST, + RESPONSE_ACTIONS_HISTORY, +} from '../../app/translations'; const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, @@ -18,7 +22,7 @@ const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.eventFilters]: EVENT_FILTERS_TAB, [AdministrationSubTab.hostIsolationExceptions]: HOST_ISOLATION_EXCEPTIONS, [AdministrationSubTab.blocklist]: BLOCKLIST, - [AdministrationSubTab.actionHistory]: ACTION_HISTORY, + [AdministrationSubTab.responseActionsHistory]: RESPONSE_ACTIONS_HISTORY, }; export function getTrailingBreadcrumbs(params: AdministrationRouteSpyState): ChromeBreadcrumb[] { diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index afad5b78e9f4..edaaa2d21a40 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -23,7 +23,7 @@ export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/:tabName export const MANAGEMENT_ROUTING_EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.eventFilters})`; export const MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.hostIsolationExceptions})`; export const MANAGEMENT_ROUTING_BLOCKLIST_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.blocklist})`; -export const MANAGEMENT_ROUTING_ACTION_HISTORY_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.actionHistory})`; +export const MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.responseActionsHistory})`; // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx index 85a5f2655dd6..655bc0a6c891 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx @@ -29,8 +29,8 @@ export const ActionLogButton = memo((p data-test-subj="responderShowActionLogButton" > {showActionLogFlyout && ( diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_empty_state.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_empty_state.tsx index a5a6f15f2de9..c5838570eeae 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_empty_state.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_empty_state.tsx @@ -26,14 +26,14 @@ export const ActionsLogEmptyState = memo( iconType="editorUnorderedList" title={

- {i18n.translate('xpack.securitySolution.actions_log.empty.title', { - defaultMessage: 'Actions history is empty', + {i18n.translate('xpack.securitySolution.responseActionsHistory.empty.title', { + defaultMessage: 'Response actions history is empty', })}

} body={
- {i18n.translate('xpack.securitySolution.actions_log.empty.content', { + {i18n.translate('xpack.securitySolution.responseActionsHistory.empty.content', { defaultMessage: 'No response actions performed', })}
@@ -44,7 +44,7 @@ export const ActionsLogEmptyState = memo( href={docLinks?.links.securitySolution.responseActions} target="_blank" > - {i18n.translate('xpack.securitySolution.actions_log.empty.link', { + {i18n.translate('xpack.securitySolution.responseActionsHistory.empty.link', { defaultMessage: 'Read more about response actions', })}
, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 549b03fedfbf..30148c9643ba 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -114,7 +114,7 @@ jest.mock('../../hooks/endpoint/use_get_endpoints_list'); const mockUseGetEndpointsList = useGetEndpointsList as jest.Mock; -describe('Response Actions Log', () => { +describe('Response actions history', () => { const testPrefix = 'response-actions-list'; let render: ( diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx index bea5a3eb07aa..2a9362830c76 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -649,7 +649,7 @@ export const ResponseActionsLog = memo<

} @@ -657,7 +657,7 @@ export const ResponseActionsLog = memo<

} diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index 542f0d72bf0b..a90f12bc2d24 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -92,7 +92,7 @@ export const TABLE_COLUMN_NAMES = Object.freeze({ export const UX_MESSAGES = Object.freeze({ flyoutTitle: (hostname: string) => i18n.translate('xpack.securitySolution.responseActionsList.flyout.title', { - defaultMessage: `Actions log : {hostname}`, + defaultMessage: `Response actions history : {hostname}`, values: { hostname }, }), pageSubTitle: i18n.translate('xpack.securitySolution.responseActionsList.list.pageSubTitle', { diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts index 54c729e41911..03cfee736def 100644 --- a/x-pack/plugins/security_solution/public/management/links.ts +++ b/x-pack/plugins/security_solution/public/management/links.ts @@ -20,7 +20,7 @@ import { HOST_ISOLATION_EXCEPTIONS_PATH, MANAGE_PATH, POLICIES_PATH, - ACTION_HISTORY_PATH, + RESPONSE_ACTIONS_HISTORY_PATH, RULES_CREATE_PATH, RULES_PATH, SecurityPageName, @@ -36,7 +36,7 @@ import { HOST_ISOLATION_EXCEPTIONS, MANAGE, POLICIES, - ACTION_HISTORY, + RESPONSE_ACTIONS_HISTORY, RULES, TRUSTED_APPLICATIONS, } from '../app/translations'; @@ -77,7 +77,7 @@ const categories = [ SecurityPageName.eventFilters, SecurityPageName.hostIsolationExceptions, SecurityPageName.blocklist, - SecurityPageName.actionHistory, + SecurityPageName.responseActionsHistory, ], }, ...cloudSecurityPostureCategories, @@ -212,13 +212,13 @@ export const links: LinkItem = { hideTimeline: true, }, { - id: SecurityPageName.actionHistory, - title: ACTION_HISTORY, + id: SecurityPageName.responseActionsHistory, + title: RESPONSE_ACTIONS_HISTORY, description: i18n.translate('xpack.securitySolution.appLinks.actionHistoryDescription', { defaultMessage: 'View the history of response actions performed on hosts.', }), landingIcon: IconActionHistory, - path: ACTION_HISTORY_PATH, + path: RESPONSE_ACTIONS_HISTORY_PATH, skipUrlState: true, hideTimeline: true, }, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx index 911d3b12931a..6fc8a99ee732 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx @@ -80,7 +80,7 @@ describe('When using the Endpoint Details Actions Menu', () => { }; }); - it('should not show the actions log link', async () => { + it('should not show the response actions history link', async () => { await render(); expect(renderResult.queryByTestId('actionsLink')).toBeNull(); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index b3e1ce76480f..40fd81c4ab58 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -156,8 +156,8 @@ export const useEndpointActionItems = ( href: getAppUrl({ path: endpointActionsPath }), children: ( ), }, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index bbee0e695a8d..b85ad2cc7f6a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -1116,7 +1116,7 @@ describe('when on the endpoint list page', () => { expect(responderButton).not.toHaveAttribute('disabled'); }); - it('navigates to the Actions log flyout', async () => { + it('navigates to the Response actions history flyout', async () => { const actionsLink = await renderResult.findByTestId('actionsLink'); expect(actionsLink.getAttribute('href')).toEqual( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts index 30001f41c363..9ec5b0eee65c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts @@ -12,8 +12,8 @@ export const OVERVIEW = i18n.translate('xpack.securitySolution.endpointDetails.o }); export const ACTIVITY_LOG = { - tabTitle: i18n.translate('xpack.securitySolution.endpointDetails.activityLog', { - defaultMessage: 'Actions Log', + tabTitle: i18n.translate('xpack.securitySolution.endpointDetails.responseActionsHistory', { + defaultMessage: 'Response actions history', }), LogEntry: { endOfLog: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index 2a54557b0095..dd06a838a26c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -17,7 +17,7 @@ import { MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, MANAGEMENT_ROUTING_BLOCKLIST_PATH, - MANAGEMENT_ROUTING_ACTION_HISTORY_PATH, + MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH, } from '../common/constants'; import { NotFoundPage } from '../../app/404'; import { EndpointsContainer } from './endpoint_hosts'; @@ -69,9 +69,9 @@ const HostIsolationExceptionsTelemetry = () => ( ); const ResponseActionsTelemetry = () => ( - + - + ); @@ -103,7 +103,10 @@ export const ManagementContainer = memo(() => { component={HostIsolationExceptionsTelemetry} /> - + diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx index 0d3f029cc34c..25d4fa117da6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/index.tsx @@ -7,7 +7,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/kibana-react-plugin/public'; import React, { memo } from 'react'; -import { MANAGEMENT_ROUTING_ACTION_HISTORY_PATH } from '../../common/constants'; +import { MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH } from '../../common/constants'; import { NotFoundPage } from '../../../app/404'; import { ResponseActionsListPage } from './view/response_actions_list_page'; @@ -15,7 +15,7 @@ export const ResponseActionsContainer = memo(() => { return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx index 734db973826c..7b0132a4b4be 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx @@ -112,7 +112,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { jest.mock('../../../hooks/endpoint/use_get_endpoints_list'); const mockUseGetEndpointsList = useGetEndpointsList as jest.Mock; -describe('Action history page', () => { +describe('Response actions history page', () => { const testPrefix = 'response-actions-list'; let render: () => ReturnType; @@ -164,7 +164,7 @@ describe('Action history page', () => { describe('Hide/Show header', () => { it('should show header when data is in', () => { reactTestingLibrary.act(() => { - history.push('/administration/action_history?page=3&pageSize=20'); + history.push('/administration/response_actions_history?page=3&pageSize=20'); }); render(); const { getByTestId } = renderResult; @@ -173,7 +173,7 @@ describe('Action history page', () => { it('should not show header when there is no actions index', () => { reactTestingLibrary.act(() => { - history.push('/administration/action_history?page=3&pageSize=20'); + history.push('/administration/response_actions_history?page=3&pageSize=20'); }); mockUseGetEndpointActionList = { ...baseMockedActionList, @@ -190,7 +190,7 @@ describe('Action history page', () => { describe('Read from URL params', () => { it('should read and set paging values from URL params', () => { reactTestingLibrary.act(() => { - history.push('/administration/action_history?page=3&pageSize=20'); + history.push('/administration/response_actions_history?page=3&pageSize=20'); }); render(); const { getByTestId } = renderResult; @@ -203,7 +203,7 @@ describe('Action history page', () => { it('should read and set command filter values from URL params', () => { const filterPrefix = 'actions-filter'; reactTestingLibrary.act(() => { - history.push('/administration/action_history?commands=release,processes'); + history.push('/administration/response_actions_history?commands=release,processes'); }); render(); @@ -240,7 +240,7 @@ describe('Action history page', () => { const filterPrefix = 'hosts-filter'; reactTestingLibrary.act(() => { history.push( - '/administration/action_history?hosts=agent-id-1,agent-id-2,agent-id-4,agent-id-5' + '/administration/response_actions_history?hosts=agent-id-1,agent-id-2,agent-id-4,agent-id-5' ); }); @@ -270,7 +270,7 @@ describe('Action history page', () => { it('should read and set status filter values from URL params', () => { const filterPrefix = 'statuses-filter'; reactTestingLibrary.act(() => { - history.push('/administration/action_history?statuses=pending,failed'); + history.push('/administration/response_actions_history?statuses=pending,failed'); }); render(); @@ -293,7 +293,7 @@ describe('Action history page', () => { it('should set selected users search input strings to URL params ', () => { const filterPrefix = 'users-filter'; reactTestingLibrary.act(() => { - history.push('/administration/action_history?users=userX,userY'); + history.push('/administration/response_actions_history?users=userX,userY'); }); render(); @@ -305,7 +305,7 @@ describe('Action history page', () => { it('should read and set relative date ranges filter values from URL params', () => { reactTestingLibrary.act(() => { - history.push('/administration/action_history?startDate=now-23m&endDate=now-1m'); + history.push('/administration/response_actions_history?startDate=now-23m&endDate=now-1m'); }); render(); @@ -324,7 +324,9 @@ describe('Action history page', () => { const startDate = '2022-09-12T11:00:00.000Z'; const endDate = '2022-09-12T11:30:33.000Z'; reactTestingLibrary.act(() => { - history.push(`/administration/action_history?startDate=${startDate}&endDate=${endDate}`); + history.push( + `/administration/response_actions_history?startDate=${startDate}&endDate=${endDate}` + ); }); const { getByTestId } = render(); diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx index e1f3705b4489..d1ae96099387 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useCallback } from 'react'; -import { ACTION_HISTORY } from '../../../../app/translations'; +import { RESPONSE_ACTIONS_HISTORY } from '../../../../app/translations'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { ResponseActionsLog } from '../../../components/endpoint_response_actions_list/response_actions_log'; import { UX_MESSAGES } from '../../../components/endpoint_response_actions_list/translations'; @@ -19,7 +19,7 @@ export const ResponseActionsListPage = () => { return ( diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index 96c1983c8f25..4ca2c34dfcc1 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -31,7 +31,7 @@ export enum AdministrationSubTab { eventFilters = 'event_filters', hostIsolationExceptions = 'host_isolation_exceptions', blocklist = 'blocklist', - actionHistory = 'action_history', + responseActionsHistory = 'response_actions_history', } /** diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 7b619c78eaf9..04f32940b161 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25489,7 +25489,6 @@ "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, =0 {ligne} =1 {ligne} other {lignes}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, =1 {utilisateur} other {utilisateurs}}", "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "Appuyer", - "xpack.securitySolution.actionLogButton.label": "Log des actions", "xpack.securitySolution.actionsContextMenu.label": "Ouvrir", "xpack.securitySolution.administration.os.linux": "Linux", "xpack.securitySolution.administration.os.macos": "Mac", @@ -27264,7 +27263,6 @@ "xpack.securitySolution.effectedPolicySelect.assignmentSectionTitle": "Affectation", "xpack.securitySolution.effectedPolicySelect.viewPolicyLinkLabel": "Afficher la politique", "xpack.securitySolution.emptyString.emptyStringDescription": "Chaîne vide", - "xpack.securitySolution.endpoint.actions.actionsLog": "Afficher le log d’actions", "xpack.securitySolution.endpoint.actions.agentDetails": "Afficher les détails de l'agent", "xpack.securitySolution.endpoint.actions.agentPolicy": "Afficher la politique de l'agent", "xpack.securitySolution.endpoint.actions.agentPolicyReassign": "Réaffecter la politique de l'agent", @@ -27732,7 +27730,6 @@ "xpack.securitySolution.endpointConsoleCommands.suspendProcess.commandArgAbout": "Commentaire qui doit accompagner l'action", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.entityId.arg.comment": "Un ID d’entité représentant le processus à suspendre", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.pid.arg.comment": "Un PID représentant le processus à suspendre", - "xpack.securitySolution.endpointDetails.activityLog": "Log d’actions", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.endOfLog": "Pas d'autre élément à afficher", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointIsolateAction": "n'a pas pu envoyer la requête : Isoler l'hôte", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointReleaseAction": "n'a pas pu envoyer la requête : Libérer l'hôte", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 214fe1234d7d..672dcbd3d5cc 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25464,7 +25464,6 @@ "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, other {ユーザー}}", "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "プレス", - "xpack.securitySolution.actionLogButton.label": "アクションログ", "xpack.securitySolution.actionsContextMenu.label": "開く", "xpack.securitySolution.administration.os.linux": "Linux", "xpack.securitySolution.administration.os.macos": "Mac", @@ -27239,7 +27238,6 @@ "xpack.securitySolution.effectedPolicySelect.assignmentSectionTitle": "割り当て", "xpack.securitySolution.effectedPolicySelect.viewPolicyLinkLabel": "ポリシーを表示", "xpack.securitySolution.emptyString.emptyStringDescription": "空の文字列", - "xpack.securitySolution.endpoint.actions.actionsLog": "アクションログを表示", "xpack.securitySolution.endpoint.actions.agentDetails": "エージェント詳細を表示", "xpack.securitySolution.endpoint.actions.agentPolicy": "エージェントポリシーを表示", "xpack.securitySolution.endpoint.actions.agentPolicyReassign": "エージェントポリシーを再割り当て", @@ -27707,7 +27705,6 @@ "xpack.securitySolution.endpointConsoleCommands.suspendProcess.commandArgAbout": "アクションに関するコメント", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.entityId.arg.comment": "一時停止するプロセスを表すエンティティID", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.pid.arg.comment": "一時停止するプロセスを表すPID", - "xpack.securitySolution.endpointDetails.activityLog": "アクションログ", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.endOfLog": "表示する情報がありません", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointIsolateAction": "要求を送信できませんでした。ホストの分離", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointReleaseAction": "要求を送信できませんでした。ホストのリリース", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index aeaadfcb1fab..0f15a16ae14a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25495,7 +25495,6 @@ "xpack.securitySolution.usersTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.usersTable.unit": "{totalCount, plural, other {个用户}}", "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "按", - "xpack.securitySolution.actionLogButton.label": "操作日志", "xpack.securitySolution.actionsContextMenu.label": "打开", "xpack.securitySolution.administration.os.linux": "Linux", "xpack.securitySolution.administration.os.macos": "Mac", @@ -27270,7 +27269,6 @@ "xpack.securitySolution.effectedPolicySelect.assignmentSectionTitle": "分配", "xpack.securitySolution.effectedPolicySelect.viewPolicyLinkLabel": "查看策略", "xpack.securitySolution.emptyString.emptyStringDescription": "空字符串", - "xpack.securitySolution.endpoint.actions.actionsLog": "查看操作日志", "xpack.securitySolution.endpoint.actions.agentDetails": "查看代理详情", "xpack.securitySolution.endpoint.actions.agentPolicy": "查看代理策略", "xpack.securitySolution.endpoint.actions.agentPolicyReassign": "重新分配代理策略", @@ -27738,7 +27736,6 @@ "xpack.securitySolution.endpointConsoleCommands.suspendProcess.commandArgAbout": "此操作将伴随一条注释", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.entityId.arg.comment": "表示要挂起的进程的实体 ID", "xpack.securitySolution.endpointConsoleCommands.suspendProcess.pid.arg.comment": "表示要挂起的进程的 PID", - "xpack.securitySolution.endpointDetails.activityLog": "操作日志", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.endOfLog": "没有更多要显示的内容", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointIsolateAction": "无法提交请求:隔离主机", "xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointReleaseAction": "无法提交请求:释放主机", From 7688fe3b58a05603919e65257dbbf6b0ba1241a2 Mon Sep 17 00:00:00 2001 From: Luke Gmys Date: Tue, 27 Sep 2022 14:58:33 +0200 Subject: [PATCH 004/185] [TIP] Loading state changes and react-query integration (#141102) --- .../public/common/mocks/story_providers.tsx | 21 +- .../public/common/mocks/test_providers.tsx | 31 ++- .../public/common/utils/barchart.test.ts | 2 +- .../public/common/utils/barchart.ts | 4 +- .../indicators_barchart.stories.tsx | 2 +- .../indicators_barchart.test.tsx | 2 +- .../indicators_barchart.tsx | 2 +- .../indicators_barchart_wrapper.stories.tsx | 2 +- .../indicators_table/cell_actions.tsx | 2 +- .../indicators_table.stories.tsx | 4 +- .../indicators_table.test.tsx | 6 +- .../indicators_table/indicators_table.tsx | 13 +- .../hooks/use_aggregated_indicators.test.tsx | 176 +++++------- .../hooks/use_aggregated_indicators.ts | 197 +++---------- .../indicators/hooks/use_indicators.test.tsx | 260 ++++-------------- .../indicators/hooks/use_indicators.ts | 186 ++++--------- .../hooks/use_indicators_total_count.tsx | 2 +- .../indicators/indicators_page.test.tsx | 3 +- .../modules/indicators/indicators_page.tsx | 70 ++--- .../fetch_aggregated_indicators.test.ts | 117 ++++++++ .../services/fetch_aggregated_indicators.ts | 125 +++++++++ .../services/fetch_indicators.test.ts | 105 +++++++ .../indicators/services/fetch_indicators.ts | 84 ++++++ .../utils/get_indicator_query_params.ts | 70 +++++ .../indicators/utils/get_indicators_query.ts | 50 ---- .../public/modules/indicators/utils/search.ts | 75 +++++ .../components/query_bar/query_bar.tsx | 13 +- .../scripts/generate_indicators.js | 4 +- 28 files changed, 894 insertions(+), 734 deletions(-) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.test.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicator_query_params.ts delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicators_query.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/utils/search.ts diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx index 78c064f72b44..eea2596327fb 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx @@ -11,6 +11,8 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { CoreStart, IUiSettingsClient } from '@kbn/core/public'; import { TimelinesUIStart } from '@kbn/timelines-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { mockIndicatorsFiltersContext } from './mock_indicators_filters_context'; import { SecuritySolutionContext } from '../../containers/security_solution_context'; import { getSecuritySolutionContextMock } from './mock_security_context'; @@ -20,6 +22,7 @@ import { generateFieldTypeMap } from './mock_field_type_map'; import { mockUiSettingsService } from './mock_kibana_ui_settings_service'; import { mockKibanaTimelinesService } from './mock_kibana_timelines_service'; import { mockTriggersActionsUiService } from './mock_kibana_triggers_actions_ui_service'; +import { InspectorContext } from '../../containers/inspector'; export interface KibanaContextMock { /** @@ -81,13 +84,17 @@ export const StoryProvidersComponent: VFC = ({ return ( - - - - {children} - - - + + + + + + {children} + + + + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx index 7e046e214b54..a93b6bfe3046 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx @@ -17,6 +17,7 @@ import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks import { createTGridMocks } from '@kbn/timelines-plugin/public/mock'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { KibanaContext } from '../../hooks/use_kibana'; import { SecuritySolutionPluginContext } from '../../types'; import { getSecuritySolutionContextMock } from './mock_security_context'; @@ -128,27 +129,31 @@ export const mockedServices = { export const TestProvidersComponent: FC = ({ children }) => ( - - - - - - - {children} - - - - - - + + + + + + + + {children} + + + + + + + ); export type MockedSearch = jest.Mocked; export type MockedTimefilter = jest.Mocked; export type MockedTriggersActionsUi = jest.Mocked; +export type MockedQueryService = jest.Mocked; export const mockedSearchService = mockedServices.data.search as MockedSearch; +export const mockedQueryService = mockedServices.data.query as MockedQueryService; export const mockedTimefilterService = mockedServices.data.query.timefilter as MockedTimefilter; export const mockedTriggersActionsUiService = mockedServices.triggersActionsUi as MockedTriggersActionsUi; diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts index 1acf8d021334..004071059a73 100644 --- a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts +++ b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { Aggregation } from '../../modules/indicators/services/fetch_aggregated_indicators'; import { convertAggregationToChartSeries } from './barchart'; -import { Aggregation } from '../../modules/indicators/hooks/use_aggregated_indicators'; const aggregation1: Aggregation = { events: { diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts index 93f6b4ce6fd6..c994e7e9f3a3 100644 --- a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts +++ b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { +import type { Aggregation, AggregationValue, ChartSeries, -} from '../../modules/indicators/hooks/use_aggregated_indicators'; +} from '../../modules/indicators/services/fetch_aggregated_indicators'; /** * Converts data received from an Elastic search with date_histogram aggregation enabled to something usable in the "@elastic/chart" BarChart component diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx index 465ef3bd1ab7..bb0a1b1205b5 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx @@ -11,8 +11,8 @@ import { Story } from '@storybook/react'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; -import { ChartSeries } from '../../hooks/use_aggregated_indicators'; import { IndicatorsBarChart } from './indicators_barchart'; +import { ChartSeries } from '../../services/fetch_aggregated_indicators'; const mockIndicators: ChartSeries[] = [ { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx index 38de7df2b3d4..19542bdf200a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { render } from '@testing-library/react'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { ChartSeries } from '../../hooks/use_aggregated_indicators'; import { IndicatorsBarChart } from './indicators_barchart'; +import { ChartSeries } from '../../services/fetch_aggregated_indicators'; moment.suppressDeprecationWarnings = true; moment.tz.setDefault('UTC'); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx index 75d731b23c3b..d5535f53a862 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx @@ -11,7 +11,7 @@ import { EuiThemeProvider } from '@elastic/eui'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; import { IndicatorBarchartLegendAction } from '../indicator_barchart_legend_action/indicator_barchart_legend_action'; import { barChartTimeAxisLabelFormatter } from '../../../../common/utils/dates'; -import { ChartSeries } from '../../hooks/use_aggregated_indicators'; +import type { ChartSeries } from '../../services/fetch_aggregated_indicators'; const ID = 'tiIndicator'; const DEFAULT_CHART_HEIGHT = '200px'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx index a9eec2afcf19..213c750c5d1e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx @@ -16,9 +16,9 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { IUiSettingsClient } from '@kbn/core/public'; import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; -import { Aggregation, AGGREGATION_NAME } from '../../hooks/use_aggregated_indicators'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; +import { Aggregation, AGGREGATION_NAME } from '../../services/fetch_aggregated_indicators'; export default { component: IndicatorsBarChartWrapper, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx index 38f22fe34556..d10ba709bfa2 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx @@ -9,11 +9,11 @@ import React, { VFC } from 'react'; import { EuiDataGridColumnCellActionProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; import { ComponentType } from '../../../../../common/types/component_type'; import { Indicator } from '../../../../../common/types/indicator'; -import { Pagination } from '../../hooks/use_indicators'; import { AddToTimeline } from '../../../timeline/components/add_to_timeline'; import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../utils/field_value'; import { FilterIn } from '../../../query_bar/components/filter_in'; import { FilterOut } from '../../../query_bar/components/filter_out'; +import type { Pagination } from '../../services/fetch_indicators'; export const CELL_TIMELINE_BUTTON_TEST_ID = 'tiIndicatorsTableCellTimelineButton'; export const CELL_FILTER_IN_BUTTON_TEST_ID = 'tiIndicatorsTableCellFilterInButton'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx index 1d563141952e..034fc1363043 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx @@ -44,7 +44,7 @@ export function WithIndicators() { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx index 71786878bdae..b110c0f91c31 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx @@ -22,7 +22,7 @@ const tableProps: IndicatorsTableProps = { indicators: [], pagination: { pageSize: 10, pageIndex: 0, pageSizeOptions: [10] }, indicatorCount: 0, - loading: false, + isLoading: false, browserFields: {}, indexPattern: { fields: [], title: '' } as SecuritySolutionDataViewBase, columnSettings: { @@ -60,7 +60,7 @@ describe('', () => { await act(async () => { render( - + ); }); @@ -74,7 +74,7 @@ describe('', () => { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx index 3e3035c8c43e..f12e080b000b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx @@ -24,11 +24,11 @@ import { cellRendererFactory } from './cell_renderer'; import { EmptyState } from '../../../../components/empty_state'; import { IndicatorsTableContext, IndicatorsTableContextValue } from './context'; import { IndicatorsFlyout } from '../indicators_flyout/indicators_flyout'; -import { Pagination } from '../../hooks/use_indicators'; import { useToolbarOptions } from './hooks/use_toolbar_options'; import { ColumnSettingsValue } from './hooks/use_column_settings'; import { useFieldTypes } from '../../../../hooks/use_field_types'; import { getFieldSchema } from '../../utils/get_field_schema'; +import { Pagination } from '../../services/fetch_indicators'; export interface IndicatorsTableProps { indicators: Indicator[]; @@ -36,7 +36,10 @@ export interface IndicatorsTableProps { pagination: Pagination; onChangeItemsPerPage: (value: number) => void; onChangePage: (value: number) => void; - loading: boolean; + /** + * If true, no data is available yet + */ + isLoading: boolean; indexPattern: SecuritySolutionDataViewBase; browserFields: BrowserFields; columnSettings: ColumnSettingsValue; @@ -57,7 +60,7 @@ export const IndicatorsTable: VFC = ({ onChangePage, onChangeItemsPerPage, pagination, - loading, + isLoading, browserFields, columnSettings: { columns, columnVisibility, handleResetColumns, handleToggleColumn, sorting }, }) => { @@ -137,7 +140,7 @@ export const IndicatorsTable: VFC = ({ ); const gridFragment = useMemo(() => { - if (loading) { + if (isLoading) { return ( @@ -177,7 +180,7 @@ export const IndicatorsTable: VFC = ({ mappedColumns, indicatorCount, leadingControlColumns, - loading, + isLoading, onChangeItemsPerPage, onChangePage, pagination, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx index be8ea27374a1..6b3feb740690 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx @@ -5,34 +5,18 @@ * 2.0. */ -import moment from 'moment'; -import { BehaviorSubject, throwError } from 'rxjs'; -import { renderHook } from '@testing-library/react-hooks'; -import { IKibanaSearchResponse, TimeRangeBounds } from '@kbn/data-plugin/common'; -import { - AGGREGATION_NAME, - RawAggregatedIndicatorsResponse, - useAggregatedIndicators, - UseAggregatedIndicatorsParam, -} from './use_aggregated_indicators'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useAggregatedIndicators, UseAggregatedIndicatorsParam } from './use_aggregated_indicators'; import { DEFAULT_TIME_RANGE } from '../../query_bar/hooks/use_filters/utils'; import { - TestProvidersComponent, - mockedSearchService, mockedTimefilterService, + TestProvidersComponent, } from '../../../common/mocks/test_providers'; import { useFilters } from '../../query_bar/hooks/use_filters'; +import { createFetchAggregatedIndicators } from '../services/fetch_aggregated_indicators'; -jest.mock('../../query_bar/hooks/use_filters/use_filters'); - -const aggregationResponse = { - rawResponse: { aggregations: { [AGGREGATION_NAME]: { buckets: [] } } }, -}; - -const calculateBoundsResponse: TimeRangeBounds = { - min: moment('1 Jan 2022 06:00:00 GMT'), - max: moment('1 Jan 2022 12:00:00 GMT'), -}; +jest.mock('../services/fetch_aggregated_indicators'); +jest.mock('../../query_bar/hooks/use_filters'); const useAggregatedIndicatorsParams: UseAggregatedIndicatorsParam = { timeRange: DEFAULT_TIME_RANGE, @@ -40,108 +24,78 @@ const useAggregatedIndicatorsParams: UseAggregatedIndicatorsParam = { const stub = () => {}; +const renderUseAggregatedIndicators = () => + renderHook((props) => useAggregatedIndicators(props), { + initialProps: useAggregatedIndicatorsParams, + wrapper: TestProvidersComponent, + }); + +const initialFiltersValue = { + filters: [], + filterQuery: { language: 'kuery', query: '' }, + filterManager: {} as any, + handleSavedQuery: stub, + handleSubmitQuery: stub, + handleSubmitTimeRange: stub, +}; + describe('useAggregatedIndicators()', () => { beforeEach(jest.clearAllMocks); - beforeEach(() => { - mockedSearchService.search.mockReturnValue(new BehaviorSubject(aggregationResponse)); - mockedTimefilterService.timefilter.calculateBounds.mockReturnValue(calculateBoundsResponse); - }); + type MockedCreateFetchAggregatedIndicators = jest.MockedFunction< + typeof createFetchAggregatedIndicators + >; + let aggregatedIndicatorsQuery: jest.MockedFunction< + ReturnType + >; - describe('when mounted', () => { - beforeEach(() => { - (useFilters as jest.MockedFunction).mockReturnValue({ - filters: [], - filterQuery: { language: 'kuery', query: '' }, - filterManager: {} as any, - handleSavedQuery: stub, - handleSubmitQuery: stub, - handleSubmitTimeRange: stub, - }); - - renderHook(() => useAggregatedIndicators(useAggregatedIndicatorsParams), { - wrapper: TestProvidersComponent, - }); - }); + beforeEach(jest.clearAllMocks); - it('should query the database for threat indicators', async () => { - expect(mockedSearchService.search).toHaveBeenCalledTimes(1); - }); + beforeEach(() => { + aggregatedIndicatorsQuery = jest.fn(); + (createFetchAggregatedIndicators as MockedCreateFetchAggregatedIndicators).mockReturnValue( + aggregatedIndicatorsQuery + ); - it('should use the calculateBounds to convert TimeRange to TimeRangeBounds', () => { - expect(mockedTimefilterService.timefilter.calculateBounds).toHaveBeenCalledTimes(1); - }); + (useFilters as jest.MockedFunction).mockReturnValue(initialFiltersValue); }); - describe('when query fails', () => { - beforeEach(async () => { - mockedSearchService.search.mockReturnValue(throwError(() => new Error('some random error'))); - mockedTimefilterService.timefilter.calculateBounds.mockReturnValue(calculateBoundsResponse); - }); + it('should create and call the aggregatedIndicatorsQuery correctly', async () => { + aggregatedIndicatorsQuery.mockResolvedValue([]); - beforeEach(() => { - renderHook(() => useAggregatedIndicators(useAggregatedIndicatorsParams), { - wrapper: TestProvidersComponent, - }); - }); + const { rerender } = renderUseAggregatedIndicators(); - it('should show an error', async () => { - expect(mockedSearchService.showError).toHaveBeenCalledTimes(1); - - expect(mockedSearchService.search).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - aggregations: expect.any(Object), - query: expect.any(Object), - size: expect.any(Number), - fields: expect.any(Array), - }), - }), - }), - expect.objectContaining({ - abortSignal: expect.any(AbortSignal), - }) - ); - }); - }); + // indicators service and the query should be called just once + expect( + createFetchAggregatedIndicators as MockedCreateFetchAggregatedIndicators + ).toHaveBeenCalledTimes(1); + expect(aggregatedIndicatorsQuery).toHaveBeenCalledTimes(1); - describe('when query is successful', () => { - beforeEach(async () => { - mockedSearchService.search.mockReturnValue( - new BehaviorSubject>({ - rawResponse: { - aggregations: { - [AGGREGATION_NAME]: { - buckets: [ - { - doc_count: 1, - key: '[Filebeat] AbuseCH Malware', - events: { - buckets: [ - { - doc_count: 0, - key: 1641016800000, - key_as_string: '1 Jan 2022 06:00:00 GMT', - }, - ], - }, - }, - ], - }, - }, - }, - }) - ); - mockedTimefilterService.timefilter.calculateBounds.mockReturnValue(calculateBoundsResponse); + // Ensure the timefilter service is called + expect(mockedTimefilterService.timefilter.calculateBounds).toHaveBeenCalled(); + // Call the query function + expect(aggregatedIndicatorsQuery).toHaveBeenLastCalledWith( + expect.objectContaining({ + filterQuery: { language: 'kuery', query: '' }, + }), + expect.any(AbortSignal) + ); + + // After filter values change, the hook will be re-rendered and should call the query function again, with + // updated values + (useFilters as jest.MockedFunction).mockReturnValue({ + ...initialFiltersValue, + filterQuery: { language: 'kuery', query: "threat.indicator.type: 'file'" }, }); - it('should call mapping function on every hit', async () => { - const { result } = renderHook(() => useAggregatedIndicators(useAggregatedIndicatorsParams), { - wrapper: TestProvidersComponent, - }); + await act(async () => rerender()); - expect(result.current.indicators.length).toEqual(1); - }); + expect(aggregatedIndicatorsQuery).toHaveBeenCalledTimes(2); + expect(aggregatedIndicatorsQuery).toHaveBeenLastCalledWith( + expect.objectContaining({ + filterQuery: { language: 'kuery', query: "threat.indicator.type: 'file'" }, + }), + expect.any(AbortSignal) + ); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts index ab583fb0ed95..2609dbda5eb1 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts @@ -6,25 +6,20 @@ */ import { TimeRange } from '@kbn/es-query'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Subscription } from 'rxjs'; -import { - IEsSearchRequest, - IKibanaSearchResponse, - isCompleteResponse, - isErrorResponse, - TimeRangeBounds, -} from '@kbn/data-plugin/common'; +import { useMemo, useState } from 'react'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { useQuery } from '@tanstack/react-query'; import { useInspector } from '../../../hooks/use_inspector'; import { useFilters } from '../../query_bar/hooks/use_filters'; -import { convertAggregationToChartSeries } from '../../../common/utils/barchart'; import { RawIndicatorFieldId } from '../../../../common/types/indicator'; -import { calculateBarchartColumnTimeInterval } from '../../../common/utils/dates'; import { useKibana } from '../../../hooks/use_kibana'; import { DEFAULT_TIME_RANGE } from '../../query_bar/hooks/use_filters/utils'; import { useSourcererDataView } from './use_sourcerer_data_view'; -import { getRuntimeMappings } from '../utils/get_runtime_mappings'; -import { getIndicatorsQuery } from '../utils/get_indicators_query'; +import { + ChartSeries, + createFetchAggregatedIndicators, + FetchAggregatedIndicatorsParams, +} from '../services/fetch_aggregated_indicators'; export interface UseAggregatedIndicatorsParam { /** @@ -55,37 +50,7 @@ export interface UseAggregatedIndicatorsValue { selectedField: string; } -export interface Aggregation { - doc_count: number; - key: string; - events: { - buckets: AggregationValue[]; - }; -} - -export interface AggregationValue { - doc_count: number; - key: number; - key_as_string: string; -} - -export interface ChartSeries { - x: string; - y: number; - g: string; -} - -const TIMESTAMP_FIELD = RawIndicatorFieldId.TimeStamp; const DEFAULT_FIELD = RawIndicatorFieldId.Feed; -export const AGGREGATION_NAME = 'barchartAggregation'; - -export interface RawAggregatedIndicatorsResponse { - aggregations: { - [AGGREGATION_NAME]: { - buckets: Aggregation[]; - }; - }; -} export const useAggregatedIndicators = ({ timeRange = DEFAULT_TIME_RANGE, @@ -100,132 +65,48 @@ export const useAggregatedIndicators = ({ const { inspectorAdapters } = useInspector(); - const searchSubscription$ = useRef(new Subscription()); - const abortController = useRef(new AbortController()); - - const [indicators, setIndicators] = useState([]); const [field, setField] = useState(DEFAULT_FIELD); const { filters, filterQuery } = useFilters(); - const dateRange: TimeRangeBounds = useMemo( - () => queryService.timefilter.timefilter.calculateBounds(timeRange), - [queryService, timeRange] + const aggregatedIndicatorsQuery = useMemo( + () => + createFetchAggregatedIndicators({ + queryService, + searchService, + inspectorAdapter: inspectorAdapters.requests, + }), + [inspectorAdapters, queryService, searchService] ); - const queryToExecute = useMemo(() => { - return getIndicatorsQuery({ timeRange, filters, filterQuery }); - }, [filterQuery, filters, timeRange]); - - const loadData = useCallback(async () => { - const dateFrom: number = (dateRange.min as moment.Moment).toDate().getTime(); - const dateTo: number = (dateRange.max as moment.Moment).toDate().getTime(); - const interval = calculateBarchartColumnTimeInterval(dateFrom, dateTo); - - const request = inspectorAdapters.requests.start('Indicator barchart', {}); - - request.stats({ - indexPattern: { - label: 'Index patterns', - value: selectedPatterns, + const { data } = useQuery( + [ + 'indicatorsBarchart', + { + filters, + field, + filterQuery, + selectedPatterns, + timeRange, }, - }); - - abortController.current = new AbortController(); - - const requestBody = { - aggregations: { - [AGGREGATION_NAME]: { - terms: { - field, - }, - aggs: { - events: { - date_histogram: { - field: TIMESTAMP_FIELD, - fixed_interval: interval, - min_doc_count: 0, - extended_bounds: { - min: dateFrom, - max: dateTo, - }, - }, - }, - }, - }, - }, - fields: [TIMESTAMP_FIELD, field], - size: 0, - query: queryToExecute, - runtime_mappings: getRuntimeMappings(), - }; - - searchSubscription$.current = searchService - .search>( - { - params: { - index: selectedPatterns, - body: requestBody, - }, - }, - { - abortSignal: abortController.current.signal, - } - ) - .subscribe({ - next: (response) => { - if (isCompleteResponse(response)) { - const aggregations: Aggregation[] = - response.rawResponse.aggregations[AGGREGATION_NAME]?.buckets; - const chartSeries: ChartSeries[] = convertAggregationToChartSeries(aggregations); - setIndicators(chartSeries); - searchSubscription$.current.unsubscribe(); - - request.stats({}).ok({ json: response }); - request.json(requestBody); - } else if (isErrorResponse(response)) { - request.error({ json: response }); - searchSubscription$.current.unsubscribe(); - } - }, - error: (requestError) => { - searchService.showError(requestError); - searchSubscription$.current.unsubscribe(); - - if (requestError instanceof Error && requestError.name.includes('Abort')) { - inspectorAdapters.requests.reset(); - } else { - request.error({ json: requestError }); - } - }, - }); - }, [ - dateRange.max, - dateRange.min, - field, - inspectorAdapters.requests, - queryToExecute, - searchService, - selectedPatterns, - ]); - - const onFieldChange = useCallback( - async (f: string) => { - setField(f); - loadData(); - }, - [loadData, setField] + ], + ({ + signal, + queryKey: [_key, queryParams], + }: { + signal?: AbortSignal; + queryKey: [string, FetchAggregatedIndicatorsParams]; + }) => aggregatedIndicatorsQuery(queryParams, signal) ); - useEffect(() => { - loadData(); - - return () => abortController.current.abort(); - }, [loadData]); + const dateRange = useMemo( + () => queryService.timefilter.timefilter.calculateBounds(timeRange), + [queryService.timefilter.timefilter, timeRange] + ); return { dateRange, - indicators, - onFieldChange, + indicators: data || [], + onFieldChange: setField, selectedField: field, }; }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx index 1b1762eb8b67..0b0620813078 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx @@ -6,17 +6,11 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; -import { - useIndicators, - RawIndicatorsResponse, - UseIndicatorsParams, - UseIndicatorsValue, -} from './use_indicators'; -import { BehaviorSubject, throwError } from 'rxjs'; -import { TestProvidersComponent, mockedSearchService } from '../../../common/mocks/test_providers'; -import { IKibanaSearchResponse } from '@kbn/data-plugin/public'; - -const indicatorsResponse = { rawResponse: { hits: { hits: [], total: 0 } } }; +import { useIndicators, UseIndicatorsParams, UseIndicatorsValue } from './use_indicators'; +import { TestProvidersComponent } from '../../../common/mocks/test_providers'; +import { createFetchIndicators } from '../services/fetch_indicators'; + +jest.mock('../services/fetch_indicators'); const useIndicatorsParams: UseIndicatorsParams = { filters: [], @@ -24,66 +18,71 @@ const useIndicatorsParams: UseIndicatorsParams = { sorting: [], }; +const indicatorsQueryResult = { indicators: [], total: 0 }; + +const renderUseIndicators = (initialProps = useIndicatorsParams) => + renderHook((props) => useIndicators(props), { + initialProps, + wrapper: TestProvidersComponent, + }); + describe('useIndicators()', () => { + type MockedCreateFetchIndicators = jest.MockedFunction; + let indicatorsQuery: jest.MockedFunction>; + beforeEach(jest.clearAllMocks); + beforeEach(() => { + indicatorsQuery = jest.fn(); + (createFetchIndicators as MockedCreateFetchIndicators).mockReturnValue(indicatorsQuery); + }); + describe('when mounted', () => { - beforeEach(() => { - mockedSearchService.search.mockReturnValue(new BehaviorSubject(indicatorsResponse)); - }); + it('should create and call the indicatorsQuery', async () => { + indicatorsQuery.mockResolvedValue(indicatorsQueryResult); - beforeEach(async () => { - renderHook( - () => useIndicators(useIndicatorsParams), - { - wrapper: TestProvidersComponent, - } - ); - }); + const hookResult = renderUseIndicators(); - it('should query the database for threat indicators', async () => { - expect(mockedSearchService.search).toHaveBeenCalledTimes(1); - }); - }); + // isLoading should be true + expect(hookResult.result.current.isLoading).toEqual(true); + + // indicators service and the query should be called just once + expect(createFetchIndicators as MockedCreateFetchIndicators).toHaveBeenCalledTimes(1); + expect(indicatorsQuery).toHaveBeenCalledTimes(1); - describe('when filters change', () => { - beforeEach(() => { - mockedSearchService.search.mockReturnValue(new BehaviorSubject(indicatorsResponse)); + // isLoading should turn to false eventually + await hookResult.waitFor(() => !hookResult.result.current.isLoading); + expect(hookResult.result.current.isLoading).toEqual(false); }); + }); + describe('when inputs change', () => { it('should query the database again and reset page to 0', async () => { - const hookResult = renderHook( - (props) => useIndicators(props), - { - initialProps: useIndicatorsParams, - wrapper: TestProvidersComponent, - } - ); + const hookResult = renderUseIndicators(); + expect(indicatorsQuery).toHaveBeenCalledTimes(1); + + // Change page + await act(async () => hookResult.result.current.onChangePage(42)); - expect(mockedSearchService.search).toHaveBeenCalledTimes(1); - expect(mockedSearchService.search).toHaveBeenLastCalledWith( + expect(indicatorsQuery).toHaveBeenCalledTimes(2); + expect(indicatorsQuery).toHaveBeenLastCalledWith( expect.objectContaining({ - params: expect.objectContaining({ body: expect.objectContaining({ from: 0 }) }), + pagination: expect.objectContaining({ pageIndex: 42 }), }), - expect.objectContaining({ - abortSignal: expect.any(AbortSignal), - }) + expect.any(AbortSignal) ); - // Change page - await act(async () => hookResult.result.current.onChangePage(42)); + // Change page size + await act(async () => hookResult.result.current.onChangeItemsPerPage(50)); - expect(mockedSearchService.search).toHaveBeenLastCalledWith( + expect(indicatorsQuery).toHaveBeenCalledTimes(3); + expect(indicatorsQuery).toHaveBeenLastCalledWith( expect.objectContaining({ - params: expect.objectContaining({ body: expect.objectContaining({ from: 42 * 25 }) }), + pagination: expect.objectContaining({ pageIndex: 0, pageSize: 50 }), }), - expect.objectContaining({ - abortSignal: expect.any(AbortSignal), - }) + expect.any(AbortSignal) ); - expect(mockedSearchService.search).toHaveBeenCalledTimes(2); - // Change filters act(() => hookResult.rerender({ @@ -92,164 +91,13 @@ describe('useIndicators()', () => { }) ); - // From range should be reset to 0 - expect(mockedSearchService.search).toHaveBeenCalledTimes(3); - expect(mockedSearchService.search).toHaveBeenLastCalledWith( + expect(indicatorsQuery).toHaveBeenLastCalledWith( expect.objectContaining({ - params: expect.objectContaining({ body: expect.objectContaining({ from: 0 }) }), - }), - expect.objectContaining({ - abortSignal: expect.any(AbortSignal), - }) - ); - }); - }); - - describe('when query fails', () => { - beforeEach(async () => { - mockedSearchService.search.mockReturnValue(throwError(() => new Error('some random error'))); - - renderHook((props) => useIndicators(props), { - initialProps: useIndicatorsParams, - wrapper: TestProvidersComponent, - }); - }); - - it('should show an error', async () => { - expect(mockedSearchService.showError).toHaveBeenCalledTimes(1); - - expect(mockedSearchService.search).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - query: expect.any(Object), - from: expect.any(Number), - size: expect.any(Number), - fields: expect.any(Array), - }), - }), + pagination: expect.objectContaining({ pageIndex: 0 }), + filterQuery: { language: 'kuery', query: "threat.indicator.type: 'file'" }, }), - expect.objectContaining({ - abortSignal: expect.any(AbortSignal), - }) + expect.any(AbortSignal) ); }); }); - - describe('when query is successful', () => { - beforeEach(async () => { - mockedSearchService.search.mockReturnValue( - new BehaviorSubject>({ - rawResponse: { hits: { hits: [{ fields: {} }], total: 1 } }, - }) - ); - }); - - it('should call mapping function on every hit', async () => { - const { result } = renderHook( - (props) => useIndicators(props), - { - initialProps: useIndicatorsParams, - wrapper: TestProvidersComponent, - } - ); - expect(result.current.indicatorCount).toEqual(1); - }); - }); - - describe('pagination', () => { - beforeEach(async () => { - mockedSearchService.search.mockReturnValue( - new BehaviorSubject>({ - rawResponse: { hits: { hits: [{ fields: {} }], total: 1 } }, - }) - ); - }); - - describe('when page changes', () => { - it('should run the query again with pagination parameters', async () => { - const { result } = renderHook( - () => useIndicators(useIndicatorsParams), - { - wrapper: TestProvidersComponent, - } - ); - - await act(async () => { - result.current.onChangePage(42); - }); - - expect(mockedSearchService.search).toHaveBeenCalledTimes(2); - - expect(mockedSearchService.search).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - size: 25, - from: 0, - }), - }), - }), - expect.anything() - ); - - expect(mockedSearchService.search).toHaveBeenLastCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - size: 25, - from: 42 * 25, - }), - }), - }), - expect.anything() - ); - - expect(result.current.pagination.pageIndex).toEqual(42); - }); - - describe('when page size changes', () => { - it('should fetch the first page and update internal page size', async () => { - const { result } = renderHook( - () => useIndicators(useIndicatorsParams), - { - wrapper: TestProvidersComponent, - } - ); - - await act(async () => { - result.current.onChangeItemsPerPage(50); - }); - - expect(mockedSearchService.search).toHaveBeenCalledTimes(3); - - expect(mockedSearchService.search).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - size: 25, - from: 0, - }), - }), - }), - expect.anything() - ); - - expect(mockedSearchService.search).toHaveBeenLastCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - body: expect.objectContaining({ - size: 50, - from: 0, - }), - }), - }), - expect.anything() - ); - - expect(result.current.pagination.pageIndex).toEqual(0); - }); - }); - }); - }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts index f06fb03b27d5..5303b5dae06c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts @@ -5,21 +5,14 @@ * 2.0. */ -import { - IEsSearchRequest, - IKibanaSearchResponse, - isCompleteResponse, - isErrorResponse, -} from '@kbn/data-plugin/common'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import type { Subscription } from 'rxjs'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { Filter, Query, TimeRange } from '@kbn/es-query'; +import { useQuery } from '@tanstack/react-query'; import { useInspector } from '../../../hooks/use_inspector'; import { Indicator } from '../../../../common/types/indicator'; import { useKibana } from '../../../hooks/use_kibana'; import { useSourcererDataView } from './use_sourcerer_data_view'; -import { getRuntimeMappings } from '../utils/get_runtime_mappings'; -import { getIndicatorsQuery } from '../utils/get_indicators_query'; +import { createFetchIndicators, FetchParams, Pagination } from '../services/fetch_indicators'; const PAGE_SIZES = [10, 25, 50]; @@ -39,20 +32,16 @@ export interface UseIndicatorsValue { pagination: Pagination; onChangeItemsPerPage: (value: number) => void; onChangePage: (value: number) => void; - loading: boolean; -} -export interface RawIndicatorsResponse { - hits: { - hits: any[]; - total: number; - }; -} + /** + * No data loaded yet + */ + isLoading: boolean; -export interface Pagination { - pageSize: number; - pageIndex: number; - pageSizeOptions: number[]; + /** + * Data loading is in progress (see docs on `isFetching` here: https://tanstack.com/query/v4/docs/guides/queries) + */ + isFetching: boolean; } export const useIndicators = ({ @@ -70,12 +59,20 @@ export const useIndicators = ({ const { inspectorAdapters } = useInspector(); - const searchSubscription$ = useRef(); - const abortController = useRef(new AbortController()); + const onChangeItemsPerPage = useCallback( + (pageSize) => + setPagination((currentPagination) => ({ + ...currentPagination, + pageSize, + pageIndex: 0, + })), + [] + ); - const [indicators, setIndicators] = useState([]); - const [indicatorCount, setIndicatorCount] = useState(0); - const [loading, setLoading] = useState(true); + const onChangePage = useCallback( + (pageIndex) => setPagination((currentPagination) => ({ ...currentPagination, pageIndex })), + [] + ); const [pagination, setPagination] = useState({ pageIndex: 0, @@ -83,119 +80,52 @@ export const useIndicators = ({ pageSizeOptions: PAGE_SIZES, }); - const query = useMemo( - () => getIndicatorsQuery({ filters, timeRange, filterQuery }), - [filterQuery, filters, timeRange] - ); - - const loadData = useCallback( - async (from: number, size: number) => { - abortController.current = new AbortController(); - - setLoading(true); - - const request = inspectorAdapters.requests.start('Indicator search', {}); - - request.stats({ - indexPattern: { - label: 'Index patterns', - value: selectedPatterns, - }, - }); - - const requestBody = { - query, - runtime_mappings: getRuntimeMappings(), - fields: [{ field: '*', include_unmapped: true }], - size, - from, - sort: sorting.map(({ id, direction }) => ({ [id]: direction })), - }; - - searchSubscription$.current = searchService - .search>( - { - params: { - index: selectedPatterns, - body: requestBody, - }, - }, - { - abortSignal: abortController.current.signal, - } - ) - .subscribe({ - next: (response) => { - setIndicators(response.rawResponse.hits.hits); - setIndicatorCount(response.rawResponse.hits.total || 0); - - if (isCompleteResponse(response)) { - setLoading(false); - searchSubscription$.current?.unsubscribe(); - request.stats({}).ok({ json: response }); - request.json(requestBody); - } else if (isErrorResponse(response)) { - setLoading(false); - request.error({ json: response }); - searchSubscription$.current?.unsubscribe(); - } - }, - error: (requestError) => { - searchService.showError(requestError); - searchSubscription$.current?.unsubscribe(); - - if (requestError instanceof Error && requestError.name.includes('Abort')) { - inspectorAdapters.requests.reset(); - } else { - request.error({ json: requestError }); - } - - setLoading(false); - }, - }); - }, - [inspectorAdapters.requests, query, searchService, selectedPatterns, sorting] - ); - - const onChangeItemsPerPage = useCallback( - async (pageSize) => { - setPagination((currentPagination) => ({ - ...currentPagination, - pageSize, - pageIndex: 0, - })); + // Go to first page after filters are changed + useEffect(() => { + onChangePage(0); + }, [filters, filterQuery, timeRange, sorting, onChangePage]); - loadData(0, pageSize); - }, - [loadData] + const fetchIndicators = useMemo( + () => createFetchIndicators({ searchService, inspectorAdapter: inspectorAdapters.requests }), + [inspectorAdapters, searchService] ); - const onChangePage = useCallback( - async (pageIndex) => { - setPagination((currentPagination) => ({ ...currentPagination, pageIndex })); - loadData(pageIndex * pagination.pageSize, pagination.pageSize); - }, - [loadData, pagination.pageSize] + const { isLoading, isFetching, data, refetch } = useQuery( + [ + 'indicatorsTable', + { + timeRange, + filterQuery, + filters, + selectedPatterns, + sorting, + pagination, + }, + ], + ({ signal, queryKey: [_key, queryParams] }) => + fetchIndicators(queryParams as FetchParams, signal), + { + /** + * See https://tanstack.com/query/v4/docs/guides/paginated-queries + * This is essential for our ux + */ + keepPreviousData: true, + } ); const handleRefresh = useCallback(() => { onChangePage(0); - }, [onChangePage]); - - // Initial data load (on mount) - useEffect(() => { - handleRefresh(); - - return () => abortController.current.abort(); - }, [handleRefresh]); + refetch(); + }, [onChangePage, refetch]); return { - indicators, - indicatorCount, + indicators: data?.indicators || [], + indicatorCount: data?.total || 0, pagination, onChangePage, onChangeItemsPerPage, - loading, + isLoading, + isFetching, handleRefresh, }; }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_total_count.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_total_count.tsx index 9c49ad8126a2..d99d7c0fc4b0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_total_count.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_total_count.tsx @@ -12,8 +12,8 @@ import { isCompleteResponse, } from '@kbn/data-plugin/common'; import { useKibana } from '../../../hooks/use_kibana'; -import type { RawIndicatorsResponse } from './use_indicators'; import { useSourcererDataView } from './use_sourcerer_data_view'; +import type { RawIndicatorsResponse } from '../services/fetch_indicators'; export const useIndicatorsTotalCount = () => { const { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx index 371f917c2774..11bdb8fc8e6e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx @@ -35,7 +35,8 @@ describe('', () => { (useIndicators as jest.MockedFunction).mockReturnValue({ indicators: [{ fields: {} }], indicatorCount: 1, - loading: false, + isLoading: false, + isFetching: false, pagination: { pageIndex: 0, pageSize: 10, pageSizeOptions: [10] }, onChangeItemsPerPage: stub, onChangePage: stub, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx index 8c138ffec502..f51e062e1c3c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx @@ -6,6 +6,7 @@ */ import React, { FC, VFC } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { IndicatorsFilters } from './containers/indicators_filters/indicators_filters'; import { IndicatorsBarChartWrapper } from './components/indicators_barchart_wrapper/indicators_barchart_wrapper'; import { IndicatorsTable } from './components/indicators_table/indicators_table'; @@ -19,10 +20,14 @@ import { FieldTypesProvider } from '../../containers/field_types_provider'; import { InspectorProvider } from '../../containers/inspector'; import { useColumnSettings } from './components/indicators_table/hooks/use_column_settings'; +const queryClient = new QueryClient(); + const IndicatorsPageProviders: FC = ({ children }) => ( - - {children} - + + + {children} + + ); const IndicatorsPageContent: VFC = () => { @@ -49,36 +54,35 @@ const IndicatorsPageContent: VFC = () => { }); return ( - - - - - - - - - - - + + + + + + + + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts new file mode 100644 index 000000000000..c5503f1b32a0 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { mockedQueryService, mockedSearchService } from '../../../common/mocks/test_providers'; +import { BehaviorSubject, throwError } from 'rxjs'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { AGGREGATION_NAME, createFetchAggregatedIndicators } from './fetch_aggregated_indicators'; + +const aggregationResponse = { + rawResponse: { aggregations: { [AGGREGATION_NAME]: { buckets: [] } } }, +}; + +describe('FetchAggregatedIndicatorsService', () => { + beforeEach(jest.clearAllMocks); + + describe('aggregatedIndicatorsQuery()', () => { + describe('when query is successful', () => { + beforeEach(() => { + mockedSearchService.search.mockReturnValue(new BehaviorSubject(aggregationResponse)); + }); + + it('should pass the query down to searchService', async () => { + const aggregatedIndicatorsQuery = createFetchAggregatedIndicators({ + searchService: mockedSearchService, + queryService: mockedQueryService as any, + inspectorAdapter: new RequestAdapter(), + }); + + const result = await aggregatedIndicatorsQuery({ + selectedPatterns: [], + filterQuery: { language: 'kuery', query: '' }, + filters: [], + field: 'myField', + timeRange: { + from: '', + to: '', + }, + }); + + expect(mockedSearchService.search).toHaveBeenCalled(); + expect(mockedSearchService.search).toHaveBeenCalledWith( + expect.objectContaining({ + params: expect.objectContaining({ + body: expect.objectContaining({ + size: 0, + query: expect.objectContaining({ bool: expect.anything() }), + runtime_mappings: { + 'threat.indicator.name': { script: expect.anything(), type: 'keyword' }, + 'threat.indicator.name_origin': { script: expect.anything(), type: 'keyword' }, + }, + aggregations: { + [AGGREGATION_NAME]: { + terms: { + field: 'myField', + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: expect.anything(), + min_doc_count: 0, + extended_bounds: expect.anything(), + }, + }, + }, + }, + }, + fields: ['@timestamp', 'myField'], + }), + index: [], + }), + }), + expect.anything() + ); + + expect(result).toMatchInlineSnapshot(`Array []`); + }); + }); + + describe('when query fails', () => { + beforeEach(() => { + mockedSearchService.search.mockReturnValue( + throwError(() => new Error('some random exception')) + ); + }); + + it('should throw an error', async () => { + const aggregatedIndicatorsQuery = createFetchAggregatedIndicators({ + searchService: mockedSearchService, + queryService: mockedQueryService as any, + inspectorAdapter: new RequestAdapter(), + }); + + try { + await aggregatedIndicatorsQuery({ + selectedPatterns: [], + filterQuery: { language: 'kuery', query: '' }, + filters: [], + field: 'myField', + timeRange: { + from: '', + to: '', + }, + }); + } catch (error) { + expect(error).toMatchInlineSnapshot(`[Error: some random exception]`); + } + + expect.assertions(1); + }); + }); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts new file mode 100644 index 000000000000..6cf0fea18b2c --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { TimeRangeBounds } from '@kbn/data-plugin/common'; +import type { ISearchStart, QueryStart } from '@kbn/data-plugin/public'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { convertAggregationToChartSeries } from '../../../common/utils/barchart'; +import { calculateBarchartColumnTimeInterval } from '../../../common/utils/dates'; +import { RawIndicatorFieldId } from '../../../../common/types/indicator'; +import { getIndicatorQueryParams } from '../utils/get_indicator_query_params'; +import { search } from '../utils/search'; + +const TIMESTAMP_FIELD = RawIndicatorFieldId.TimeStamp; + +export const AGGREGATION_NAME = 'barchartAggregation'; + +export interface AggregationValue { + doc_count: number; + key: number; + key_as_string: string; +} + +export interface Aggregation { + doc_count: number; + key: string; + events: { + buckets: AggregationValue[]; + }; +} + +export interface RawAggregatedIndicatorsResponse { + aggregations: { + [AGGREGATION_NAME]: { + buckets: Aggregation[]; + }; + }; +} + +export interface ChartSeries { + x: string; + y: number; + g: string; +} + +export interface FetchAggregatedIndicatorsParams { + selectedPatterns: string[]; + filters: Filter[]; + filterQuery: Query; + timeRange: TimeRange; + field: string; +} + +export const createFetchAggregatedIndicators = + ({ + inspectorAdapter, + searchService, + queryService, + }: { + inspectorAdapter: RequestAdapter; + searchService: ISearchStart; + queryService: QueryStart; + }) => + async ( + { selectedPatterns, timeRange, field, filterQuery, filters }: FetchAggregatedIndicatorsParams, + signal?: AbortSignal + ): Promise => { + const dateRange: TimeRangeBounds = + queryService.timefilter.timefilter.calculateBounds(timeRange); + + const dateFrom: number = (dateRange.min as moment.Moment).toDate().getTime(); + const dateTo: number = (dateRange.max as moment.Moment).toDate().getTime(); + const interval = calculateBarchartColumnTimeInterval(dateFrom, dateTo); + + const sharedParams = getIndicatorQueryParams({ timeRange, filters, filterQuery }); + + const searchRequestBody = { + aggregations: { + [AGGREGATION_NAME]: { + terms: { + field, + }, + aggs: { + events: { + date_histogram: { + field: TIMESTAMP_FIELD, + fixed_interval: interval, + min_doc_count: 0, + extended_bounds: { + min: dateFrom, + max: dateTo, + }, + }, + }, + }, + }, + }, + fields: [TIMESTAMP_FIELD, field], + size: 0, + ...sharedParams, + }; + + const { + aggregations: { [AGGREGATION_NAME]: aggregation }, + } = await search( + searchService, + { + params: { + index: selectedPatterns, + body: searchRequestBody, + }, + }, + { signal, inspectorAdapter, requestName: 'Indicators barchart' } + ); + + const aggregations: Aggregation[] = aggregation?.buckets; + + const chartSeries: ChartSeries[] = convertAggregationToChartSeries(aggregations); + + return chartSeries; + }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.test.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.test.ts new file mode 100644 index 000000000000..388b1c9c9e7c --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { mockedSearchService } from '../../../common/mocks/test_providers'; +import { BehaviorSubject, throwError } from 'rxjs'; +import { createFetchIndicators } from './fetch_indicators'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; + +const indicatorsResponse = { rawResponse: { hits: { hits: [], total: 0 } } }; + +describe('FetchIndicatorsService', () => { + beforeEach(jest.clearAllMocks); + + describe('indicatorsQuery()', () => { + describe('when query is successful', () => { + beforeEach(() => { + mockedSearchService.search.mockReturnValue(new BehaviorSubject(indicatorsResponse)); + }); + + it('should pass the query down to searchService', async () => { + const indicatorsQuery = createFetchIndicators({ + searchService: mockedSearchService, + inspectorAdapter: new RequestAdapter(), + }); + + const result = await indicatorsQuery({ + pagination: { + pageIndex: 0, + pageSize: 25, + pageSizeOptions: [1, 2, 3], + }, + selectedPatterns: [], + sorting: [], + filterQuery: { language: 'kuery', query: '' }, + filters: [], + }); + + expect(mockedSearchService.search).toHaveBeenCalled(); + expect(mockedSearchService.search).toHaveBeenCalledWith( + expect.objectContaining({ + params: { + body: { + fields: [{ field: '*', include_unmapped: true }], + from: 0, + query: expect.objectContaining({ bool: expect.anything() }), + runtime_mappings: { + 'threat.indicator.name': { script: expect.anything(), type: 'keyword' }, + 'threat.indicator.name_origin': { script: expect.anything(), type: 'keyword' }, + }, + size: 25, + sort: [], + }, + index: [], + }, + }), + expect.anything() + ); + + expect(result).toMatchInlineSnapshot(` + Object { + "indicators": Array [], + "total": 0, + } + `); + }); + }); + + describe('when query fails', () => { + beforeEach(() => { + mockedSearchService.search.mockReturnValue( + throwError(() => new Error('some random exception')) + ); + }); + + it('should throw an error', async () => { + const indicatorsQuery = createFetchIndicators({ + searchService: mockedSearchService, + inspectorAdapter: new RequestAdapter(), + }); + + try { + await indicatorsQuery({ + pagination: { + pageIndex: 0, + pageSize: 25, + pageSizeOptions: [1, 2, 3], + }, + selectedPatterns: [], + sorting: [], + filterQuery: { language: 'kuery', query: '' }, + filters: [], + }); + } catch (error) { + expect(error).toMatchInlineSnapshot(`[Error: some random exception]`); + } + + expect.assertions(1); + }); + }); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.ts new file mode 100644 index 000000000000..f06038c32011 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_indicators.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { ISearchStart } from '@kbn/data-plugin/public'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { Indicator } from '../../../../common/types/indicator'; +import { getIndicatorQueryParams } from '../utils/get_indicator_query_params'; +import { search } from '../utils/search'; + +export interface RawIndicatorsResponse { + hits: { + hits: any[]; + total: number; + }; +} + +export interface Pagination { + pageSize: number; + pageIndex: number; + pageSizeOptions: number[]; +} + +interface FetchIndicatorsDependencies { + searchService: ISearchStart; + inspectorAdapter: RequestAdapter; +} + +export interface FetchParams { + pagination: Pagination; + selectedPatterns: string[]; + sorting: any[]; + filters: Filter[]; + timeRange?: TimeRange; + filterQuery: Query; +} + +type ReactQueryKey = [string, FetchParams]; + +export interface IndicatorsQueryParams { + signal?: AbortSignal; + queryKey: ReactQueryKey; +} + +export interface IndicatorsResponse { + indicators: Indicator[]; + total: number; +} + +export const createFetchIndicators = + ({ searchService, inspectorAdapter }: FetchIndicatorsDependencies) => + async ( + { pagination, selectedPatterns, timeRange, filterQuery, filters, sorting }: FetchParams, + signal?: AbortSignal + ): Promise => { + const sharedParams = getIndicatorQueryParams({ timeRange, filters, filterQuery }); + + const searchRequestBody = { + size: pagination.pageSize, + from: pagination.pageIndex, + fields: [{ field: '*', include_unmapped: true } as const], + sort: sorting.map(({ id, direction }) => ({ [id]: direction })), + ...sharedParams, + }; + + const { + hits: { hits: indicators, total }, + } = await search( + searchService, + { + params: { + index: selectedPatterns, + body: searchRequestBody, + }, + }, + { inspectorAdapter, requestName: 'Indicators table', signal } + ); + + return { indicators, total }; + }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicator_query_params.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicator_query_params.ts new file mode 100644 index 000000000000..bcaf304fdfd7 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicator_query_params.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { buildEsQuery, Filter, Query, TimeRange } from '@kbn/es-query'; +import { THREAT_QUERY_BASE } from '../../../../common/constants'; +import { RawIndicatorFieldId } from '../../../../common/types/indicator'; +import { threatIndicatorNamesOriginScript, threatIndicatorNamesScript } from './display_name'; + +const TIMESTAMP_FIELD = RawIndicatorFieldId.TimeStamp; + +/** + * Prepare shared `runtime_mappings` and `query` fields used within indicator search request + */ +export const getIndicatorQueryParams = ({ + filters, + filterQuery, + timeRange, +}: { + filters: Filter[]; + filterQuery: Query; + timeRange?: TimeRange; +}) => { + return { + runtime_mappings: { + [RawIndicatorFieldId.Name]: { + type: 'keyword', + script: { + source: threatIndicatorNamesScript(), + }, + }, + [RawIndicatorFieldId.NameOrigin]: { + type: 'keyword', + script: { + source: threatIndicatorNamesOriginScript(), + }, + }, + } as const, + query: buildEsQuery( + undefined, + [ + { + query: THREAT_QUERY_BASE, + language: 'kuery', + }, + { + query: filterQuery.query as string, + language: 'kuery', + }, + ], + [ + ...filters, + { + query: { + range: { + [TIMESTAMP_FIELD]: { + gte: timeRange?.from, + lte: timeRange?.to, + }, + }, + }, + meta: {}, + }, + ] + ), + }; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicators_query.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicators_query.ts deleted file mode 100644 index 160fa22db763..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/get_indicators_query.ts +++ /dev/null @@ -1,50 +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 { buildEsQuery, Filter, Query, TimeRange } from '@kbn/es-query'; -import { THREAT_QUERY_BASE } from '../../../../common/constants'; -import { RawIndicatorFieldId } from '../../../../common/types/indicator'; - -const TIMESTAMP_FIELD = RawIndicatorFieldId.TimeStamp; - -export const getIndicatorsQuery = ({ - filters, - filterQuery, - timeRange, -}: { - filters: Filter[]; - filterQuery: Query; - timeRange?: TimeRange; -}) => { - return buildEsQuery( - undefined, - [ - { - query: THREAT_QUERY_BASE, - language: 'kuery', - }, - { - query: filterQuery.query as string, - language: 'kuery', - }, - ], - [ - ...filters, - { - query: { - range: { - [TIMESTAMP_FIELD]: { - gte: timeRange?.from, - lte: timeRange?.to, - }, - }, - }, - meta: {}, - }, - ] - ); -}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/search.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/search.ts new file mode 100644 index 000000000000..49cd371680ce --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/utils/search.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + IEsSearchRequest, + IKibanaSearchResponse, + isCompleteResponse, + isErrorResponse, +} from '@kbn/data-plugin/common'; +import { ISearchStart } from '@kbn/data-plugin/public'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; + +interface SearchOptions { + /** + * Inspector adapter, available in the context + */ + inspectorAdapter: RequestAdapter; + + /** + * Request name registered in the inspector panel + */ + requestName: string; + + /** + * Abort signal + */ + signal?: AbortSignal; +} + +/** + * This is a searchService wrapper that will instrument your query with `inspector` and turn it into a Promise, + * resolved when complete result set is returned or rejected on any error, other than Abort. + */ +export const search = async ( + searchService: ISearchStart, + searchRequest: IEsSearchRequest, + { inspectorAdapter, requestName, signal }: SearchOptions +): Promise => { + const requestId = `${Date.now()}`; + const request = inspectorAdapter.start(requestName, { id: requestId }); + + return new Promise((resolve, reject) => { + searchService + .search>(searchRequest, { + abortSignal: signal, + }) + .subscribe({ + next: (response) => { + if (isCompleteResponse(response)) { + request.stats({}).ok({ json: response }); + request.json(searchRequest.params?.body || {}); + + resolve(response.rawResponse); + } else if (isErrorResponse(response)) { + request.error({ json: response }); + reject(response); + } + }, + error: (requestError) => { + if (requestError instanceof Error && requestError.name.includes('Abort')) { + inspectorAdapter.resetRequest(requestId); + } else { + request.error({ json: requestError }); + } + + searchService.showError(requestError); + reject(requestError); + }, + }); + }); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar/query_bar.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar/query_bar.tsx index d2739476d7d6..e564c898f894 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar/query_bar.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar/query_bar.tsx @@ -82,15 +82,17 @@ export const QueryBar = memo( }) => { const onQuerySubmit = useCallback( ({ query, dateRange }: QueryPayload) => { - if (isQuery(query) && !deepEqual(query, filterQuery)) { - onSubmitQuery(query); - } - if (dateRange != null) { onSubmitDateRange(dateRange); } + + if (isQuery(query) && !deepEqual(query, filterQuery)) { + onSubmitQuery(query); + } else { + onRefresh(); + } }, - [filterQuery, onSubmitDateRange, onSubmitQuery] + [filterQuery, onRefresh, onSubmitDateRange, onSubmitQuery] ); const onQueryChange = useCallback( @@ -169,7 +171,6 @@ export const QueryBar = memo( dataTestSubj={dataTestSubj} savedQuery={savedQuery} displayStyle={displayStyle} - onRefresh={onRefresh} /> ); } diff --git a/x-pack/plugins/threat_intelligence/scripts/generate_indicators.js b/x-pack/plugins/threat_intelligence/scripts/generate_indicators.js index 44e0ad166353..0d5c170965fe 100644 --- a/x-pack/plugins/threat_intelligence/scripts/generate_indicators.js +++ b/x-pack/plugins/threat_intelligence/scripts/generate_indicators.js @@ -50,7 +50,7 @@ const main = async () => { 'threat.feed.name': { type: 'keyword', }, - 'threat.indicator.url.original': { + 'threat.indicator.url.full': { type: 'keyword', }, 'threat.indicator.first_seen': { @@ -92,7 +92,7 @@ const main = async () => { 'threat.indicator.first_seen': timestamp, 'threat.feed.name': FEED_NAMES[Math.ceil(Math.random() * FEED_NAMES.length) - 1], 'threat.indicator.type': 'url', - 'threat.indicator.url.original': faker.internet.url(), + 'threat.indicator.url.full': faker.internet.url(), 'event.type': 'indicator', 'event.category': 'threat', }, From cc9827e396b166fbdad536381420272d614c0b98 Mon Sep 17 00:00:00 2001 From: Katerina Patticha Date: Tue, 27 Sep 2022 15:20:19 +0200 Subject: [PATCH 005/185] [APM] Generate Service metrics with synthrace (#141739) --- .../src/cli/run_synthtrace.ts | 4 +- .../src/cli/utils/synthtrace_worker.ts | 4 +- ...gator.ts => service_metrics_aggregator.ts} | 68 ++++++++++++++----- 3 files changed, 55 insertions(+), 21 deletions(-) rename packages/kbn-apm-synthtrace/src/lib/apm/aggregators/{service_latency_aggregator.ts => service_metrics_aggregator.ts} (78%) diff --git a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts index ecfbd4a387f3..517d2d61d799 100644 --- a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts +++ b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts @@ -15,7 +15,7 @@ import { parseRunCliFlags } from './utils/parse_run_cli_flags'; import { getCommonServices } from './utils/get_common_services'; import { ApmSynthtraceKibanaClient } from '../lib/apm/client/apm_synthtrace_kibana_client'; import { StreamAggregator } from '../lib/stream_aggregator'; -import { ServiceLatencyAggregator } from '../lib/apm/aggregators/service_latency_aggregator'; +import { ServicMetricsAggregator } from '../lib/apm/aggregators/service_metrics_aggregator'; function options(y: Argv) { return y @@ -207,7 +207,7 @@ export function runSynthtrace() { } const aggregators: StreamAggregator[] = []; const registry = new Map StreamAggregator[]>([ - ['service', () => [new ServiceLatencyAggregator()]], + ['service', () => [new ServicMetricsAggregator()]], ]); if (runOptions.streamProcessors && runOptions.streamProcessors.length > 0) { for (const processorName of runOptions.streamProcessors) { diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts index 720b1b0527e8..54ce5b1b2e32 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts @@ -16,7 +16,7 @@ import { StreamProcessor } from '../../lib/stream_processor'; import { Scenario } from '../scenario'; import { EntityIterable, Fields } from '../../..'; import { StreamAggregator } from '../../lib/stream_aggregator'; -import { ServiceLatencyAggregator } from '../../lib/apm/aggregators/service_latency_aggregator'; +import { ServicMetricsAggregator } from '../../lib/apm/aggregators/service_metrics_aggregator'; // logging proxy to main thread, ensures we see real time logging const l = { @@ -63,7 +63,7 @@ async function setup() { parentPort?.postMessage({ workerIndex, lastTimestamp: item['@timestamp'] }); } }; - const aggregators: StreamAggregator[] = [new ServiceLatencyAggregator()]; + const aggregators: StreamAggregator[] = [new ServicMetricsAggregator()]; // If we are sending data to apm-server we do not have to create any aggregates in the stream processor streamProcessor = new StreamProcessor({ version, diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts b/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts similarity index 78% rename from packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts rename to packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts index e28ba234b2a4..618c9e52b9f2 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/aggregators/service_metrics_aggregator.ts @@ -12,12 +12,14 @@ import { ApmFields } from '../apm_fields'; import { Fields } from '../../entity'; import { StreamAggregator } from '../../stream_aggregator'; -type LatencyState = { +type AggregationState = { count: number; min: number; max: number; sum: number; timestamp: number; + failure_count: number; + success_count: number; } & Pick; export type ServiceFields = Fields & @@ -35,15 +37,22 @@ export type ServiceFields = Fields & | 'transaction.type' > & Partial<{ - 'transaction.duration.aggregate': { - min: number; - max: number; - sum: number; - value_count: number; + _doc_count: number; + transaction: { + duration: { + summary: { + min: number; + max: number; + sum: number; + value_count: number; + }; + }; + failure_count: number; + success_count: number; }; }>; -export class ServiceLatencyAggregator implements StreamAggregator { +export class ServicMetricsAggregator implements StreamAggregator { public readonly name; constructor() { @@ -68,7 +77,7 @@ export class ServiceLatencyAggregator implements StreamAggregator { duration: { type: 'object', properties: { - aggregate: { + summary: { type: 'aggregate_metric_double', metrics: ['min', 'max', 'sum', 'value_count'], default_metric: 'sum', @@ -76,6 +85,12 @@ export class ServiceLatencyAggregator implements StreamAggregator { }, }, }, + failure_count: { + type: { type: 'long' }, + }, + success_count: { + type: { type: 'long' }, + }, }, }, service: { @@ -99,7 +114,7 @@ export class ServiceLatencyAggregator implements StreamAggregator { return null; } - private state: Record = {}; + private state: Record = {}; private processedComponent: number = 0; @@ -120,13 +135,25 @@ export class ServiceLatencyAggregator implements StreamAggregator { 'service.name': service, 'service.environment': environment, 'transaction.type': transactionType, + failure_count: 0, + success_count: 0, }; } + + const state = this.state[key]; + state.count++; + + switch (event['event.outcome']) { + case 'failure': + state.failure_count++; + break; + case 'success': + state.success_count++; + break; + } + const duration = Number(event['transaction.duration.us']); if (duration >= 0) { - const state = this.state[key]; - - state.count++; state.sum += duration; if (duration > state.max) state.max = duration; if (duration < state.min) state.min = Math.min(0, duration); @@ -164,17 +191,24 @@ export class ServiceLatencyAggregator implements StreamAggregator { const component = Date.now() % 100; const state = this.state[key]; return { + _doc_count: state.count, '@timestamp': state.timestamp + random(0, 100) + component + this.processedComponent, 'metricset.name': 'service', 'processor.event': 'metric', 'service.name': state['service.name'], 'service.environment': state['service.environment'], 'transaction.type': state['transaction.type'], - 'transaction.duration.aggregate': { - min: state.min, - max: state.max, - sum: state.sum, - value_count: state.count, + transaction: { + duration: { + summary: { + min: state.min, + max: state.max, + sum: state.sum, + value_count: state.count, + }, + }, + failure_count: state.failure_count, + success_count: state.success_count, }, }; } From f2937926ffd79714bf18af587d4b4b6c8fc14b6f Mon Sep 17 00:00:00 2001 From: Jan Monschke Date: Tue, 27 Sep 2022 16:27:43 +0300 Subject: [PATCH 006/185] fix: don't show process ancestry insights in some cases (#141751) --- .../event_details/insights/insights.test.tsx | 33 +++++++++++++++++-- .../event_details/insights/insights.tsx | 21 ++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx index 747b2ec939f7..b31cf1d1252b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx @@ -61,7 +61,7 @@ jest.mock('../../../hooks/use_experimental_features', () => ({ })); const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock; -const data: TimelineEventsDetailsItem[] = [ +const dataWithoutAgentType: TimelineEventsDetailsItem[] = [ { category: 'process', field: 'process.entity_id', @@ -82,6 +82,16 @@ const data: TimelineEventsDetailsItem[] = [ }, ]; +const data: TimelineEventsDetailsItem[] = [ + ...dataWithoutAgentType, + { + category: 'agent', + field: 'agent.type', + isObjectArray: false, + values: ['endpoint'], + }, +]; + describe('Insights', () => { beforeEach(() => { mockUseGetUserCasesPermissions.mockReturnValue(noCasesPermissions()); @@ -123,9 +133,11 @@ describe('Insights', () => { describe('with feature flag enabled', () => { describe('with platinum license', () => { - it('should show insights for related alerts by process ancestry', () => { + beforeAll(() => { licenseServiceMock.isPlatinumPlus.mockReturnValue(true); + }); + it('should show insights for related alerts by process ancestry', () => { render( @@ -137,6 +149,23 @@ describe('Insights', () => { screen.queryByRole('link', { name: new RegExp(i18n.ALERT_UPSELL) }) ).not.toBeInTheDocument(); }); + + describe('without process ancestry info', () => { + it('should not show the related alerts by process ancestry insights module', () => { + render( + + + + ); + + expect(screen.queryByTestId('related-alerts-by-ancestry')).not.toBeInTheDocument(); + }); + }); }); describe('without platinum license', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx index 358b2d8833a6..520f30fb9c31 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx @@ -40,7 +40,6 @@ export const Insights = React.memo( 'insightsRelatedAlertsByProcessAncestry' ); const hasAtLeastPlatinum = useLicense().isPlatinumPlus(); - const processEntityField = find({ category: 'process', field: 'process.entity_id' }, data); const originalDocumentId = find( { category: 'kibana', field: 'kibana.alert.ancestors.id' }, data @@ -49,7 +48,12 @@ export const Insights = React.memo( { category: 'kibana', field: 'kibana.alert.rule.parameters.index' }, data ); - const hasProcessEntityInfo = hasData(processEntityField); + const agentTypeField = find({ category: 'agent', field: 'agent.type' }, data); + const eventModuleField = find({ category: 'event', field: 'event.module' }, data); + const processEntityField = find({ category: 'process', field: 'process.entity_id' }, data); + const hasProcessEntityInfo = + hasData(processEntityField) && + hasCorrectAgentTypeAndEventModule(agentTypeField, eventModuleField); const processSessionField = find( { category: 'process', field: 'process.entry_leader.entity_id' }, @@ -147,4 +151,17 @@ export const Insights = React.memo( } ); +function hasCorrectAgentTypeAndEventModule( + agentTypeField?: TimelineEventsDetailsItem, + eventModuleField?: TimelineEventsDetailsItem +): boolean { + return ( + hasData(agentTypeField) && + (agentTypeField.values[0] === 'endpoint' || + (agentTypeField.values[0] === 'winlogbeat' && + hasData(eventModuleField) && + eventModuleField.values[0] === 'sysmon')) + ); +} + Insights.displayName = 'Insights'; From fb136431c5a47fe8bb9fab4672d88a80f1d8b760 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 27 Sep 2022 09:30:17 -0400 Subject: [PATCH 007/185] [Security Solution][Timeline][Fix] - Use dynamic language for search bar (#137606) * use search bar language in place of hard coded kuery * update tests * update snapshot * add cypress --- .../e2e/timelines/search_or_filter.cy.ts | 15 ++++++++++++++- .../cypress/screens/timeline.ts | 10 ++++++++++ .../cypress/tasks/timeline.ts | 19 +++++++++++++++++++ .../__snapshots__/index.test.tsx.snap | 1 + .../timeline/query_tab_content/index.test.tsx | 1 + .../timeline/query_tab_content/index.tsx | 11 +++++++++-- .../timeline/epic_local_storage.test.tsx | 1 + 7 files changed, 55 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/e2e/timelines/search_or_filter.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/timelines/search_or_filter.cy.ts index d5a084f65fac..39420b27e386 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/timelines/search_or_filter.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/timelines/search_or_filter.cy.ts @@ -16,7 +16,11 @@ import { cleanKibana } from '../../tasks/common'; import { login, visit, visitWithoutDateRange } from '../../tasks/login'; import { openTimelineUsingToggle } from '../../tasks/security_main'; -import { executeTimelineKQL } from '../../tasks/timeline'; +import { + changeTimelineQueryLanguage, + executeTimelineKQL, + executeTimelineSearch, +} from '../../tasks/timeline'; import { waitForTimelinesPanelToBeLoaded } from '../../tasks/timelines'; import { HOSTS_URL, TIMELINES_URL } from '../../urls/navigation'; @@ -38,6 +42,15 @@ describe('Timeline search and filters', () => { cy.get(SERVER_SIDE_EVENT_COUNT).should(($count) => expect(+$count.text()).to.be.gt(0)); }); + + it('executes a Lucene query', () => { + const messageProcessQuery = 'message:Process\\ zsh*'; + openTimelineUsingToggle(); + changeTimelineQueryLanguage('lucene'); + executeTimelineSearch(messageProcessQuery); + + cy.get(SERVER_SIDE_EVENT_COUNT).should(($count) => expect(+$count.text()).to.be.gt(0)); + }); }); describe('Update kqlMode for timeline', () => { diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 51ce3b38d4d6..87d70a73dbd1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -215,6 +215,16 @@ export const TIMELINE_KQLMODE_SEARCH = '[data-test-subj="kqlModePopoverSearch"]' export const TIMELINE_KQLMODE_FILTER = '[data-test-subj="kqlModePopoverFilter"]'; +export const QUERYBAR_MENU_POPOVER = '[data-test-subj="queryBarMenuPopover"]'; + +export const TIMELINE_SHOWQUERYBARMENU_BUTTON = `${TIMELINE_FLYOUT} [data-test-subj="showQueryBarMenu"]`; + +export const TIMELINE_SWITCHQUERYLANGUAGE_BUTTON = '[data-test-subj="switchQueryLanguageButton"]'; + +export const TIMELINE_LUCENELANGUAGE_BUTTON = '[data-test-subj="luceneLanguageMenuItem"]'; + +export const TIMELINE_KQLLANGUAGE_BUTTON = '[data-test-subj="kqlLanguageMenuItem"]'; + export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]'; export const TIMELINE_TITLE_INPUT = '[data-test-subj="save-timeline-title"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index fda2ea08769e..a83e1157e98d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -74,6 +74,11 @@ import { EMPTY_DROPPABLE_DATA_PROVIDER_GROUP, GET_TIMELINE_GRID_CELL, HOVER_ACTIONS, + TIMELINE_SWITCHQUERYLANGUAGE_BUTTON, + TIMELINE_SHOWQUERYBARMENU_BUTTON, + TIMELINE_LUCENELANGUAGE_BUTTON, + TIMELINE_KQLLANGUAGE_BUTTON, + TIMELINE_QUERY, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines'; import { drag, drop } from './common'; @@ -170,6 +175,16 @@ export const addFilter = (filter: TimelineFilter): Cypress.Chainable { + cy.get(TIMELINE_SHOWQUERYBARMENU_BUTTON).click(); + cy.get(TIMELINE_SWITCHQUERYLANGUAGE_BUTTON).click(); + if (language === 'lucene') { + cy.get(TIMELINE_LUCENELANGUAGE_BUTTON).click(); + } else { + cy.get(TIMELINE_KQLLANGUAGE_BUTTON).click(); + } +}; + export const addDataProvider = (filter: TimelineFilter): Cypress.Chainable> => { cy.get(TIMELINE_ADD_FIELD_BUTTON).click(); cy.get(LOADING_INDICATOR).should('not.exist'); @@ -280,6 +295,10 @@ export const executeTimelineKQL = (query: string) => { cy.get(`${SEARCH_OR_FILTER_CONTAINER} textarea`).type(`${query} {enter}`); }; +export const executeTimelineSearch = (query: string) => { + cy.get(TIMELINE_QUERY).type(`${query} {enter}`, { force: true }); +}; + export const expandFirstTimelineEventDetails = () => { cy.get(TOGGLE_TIMELINE_EXPAND_EVENT).first().click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap index 5b0757ee775f..3205a30d35c0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap @@ -305,6 +305,7 @@ In other use cases the message field can be used to concatenate different values } kqlMode="search" kqlQueryExpression=" " + kqlQueryLanguage="kuery" onEventClosed={[MockFunction]} renderCellValue={[Function]} rowRenderers={ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx index b610adbe6da5..cc48a58717ee 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx @@ -137,6 +137,7 @@ describe('Timeline', () => { itemsPerPageOptions: [5, 10, 20], kqlMode: 'search' as QueryTabContentComponentProps['kqlMode'], kqlQueryExpression: ' ', + kqlQueryLanguage: 'kuery', onEventClosed: jest.fn(), renderCellValue: DefaultCellRenderer, rowRenderers: defaultRowRenderers, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index c38304d79841..414604109e58 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -179,6 +179,7 @@ export const QueryTabContentComponent: React.FC = ({ itemsPerPageOptions, kqlMode, kqlQueryExpression, + kqlQueryLanguage, onEventClosed, renderCellValue, rowRenderers, @@ -223,8 +224,8 @@ export const QueryTabContentComponent: React.FC = ({ query: string; language: KueryFilterQueryKind; } = useMemo( - () => ({ query: kqlQueryExpression.trim(), language: 'kuery' }), - [kqlQueryExpression] + () => ({ query: kqlQueryExpression.trim(), language: kqlQueryLanguage }), + [kqlQueryExpression, kqlQueryLanguage] ); const combinedQueries = combineQueries({ @@ -493,6 +494,11 @@ const makeMapStateToProps = () => { ? ' ' : kqlQueryTimeline?.expression ?? ''; + const kqlQueryLanguage = + isEmpty(dataProviders) && timelineType === 'template' + ? 'kuery' + : kqlQueryTimeline?.kind ?? 'kuery'; + return { activeTab, columns, @@ -506,6 +512,7 @@ const makeMapStateToProps = () => { itemsPerPageOptions, kqlMode, kqlQueryExpression, + kqlQueryLanguage, showCallOutUnauthorizedMsg: getShowCallOutUnauthorizedMsg(state), show, showExpandedDetails: diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 42a7460a129d..e68530e4579c 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -78,6 +78,7 @@ describe('epicLocalStorage', () => { itemsPerPageOptions: [5, 10, 20], kqlMode: 'search' as QueryTabContentComponentProps['kqlMode'], kqlQueryExpression: '', + kqlQueryLanguage: 'kuery', onEventClosed: jest.fn(), renderCellValue: DefaultCellRenderer, rowRenderers: defaultRowRenderers, From ab00a759375efdeb7207595d934043d2d81d1ca8 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 27 Sep 2022 15:43:09 +0200 Subject: [PATCH 008/185] [Fleet] Fix error occurring when multiple package policies are upgraded (#141625) * [Fleet] Fix error occurring when multiple package policies are upgraded * Add integration test * Fix test * Improve testing Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/services/package_policy.test.ts | 152 ++++++++++++++++++ .../fleet/server/services/package_policy.ts | 33 ++-- .../server/services/package_policy_service.ts | 2 +- 3 files changed, 172 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 9bbeb8fd6723..10a076d734f8 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -666,6 +666,158 @@ describe('Package policy service', () => { ).rejects.toThrow('Saved object [abc/123] conflict'); }); + it('should fail to update if the name already exists on another policy', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + savedObjectsClient.find.mockResolvedValue({ + total: 1, + per_page: 1, + page: 1, + saved_objects: [ + { + id: 'existing-package-policy', + type: 'ingest-package-policies', + score: 1, + references: [], + version: '1.0.0', + attributes: { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: 'policy-id-1', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + }, + }, + ], + }); + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type: 'abcd', + references: [], + version: 'test', + attributes: {}, + }); + savedObjectsClient.update.mockImplementation( + async ( + type: string, + id: string, + attrs: any + ): Promise> => { + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type, + references: [], + version: 'test', + attributes: attrs, + }); + return attrs; + } + ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await expect( + packagePolicyService.update( + savedObjectsClient, + elasticsearchClient, + 'the-package-policy-id', + { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: '93c46720-c217-11ea-9906-b5b8a21b268e', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + } + ) + ).rejects.toThrow( + 'An integration policy with the name endpoint-1 already exists. Please rename it or choose a different name.' + ); + }); + + it('should not fail to update if skipUniqueNameVerification when the name already exists on another policy', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + savedObjectsClient.find.mockResolvedValue({ + total: 1, + per_page: 1, + page: 1, + saved_objects: [ + { + id: 'existing-package-policy', + type: 'ingest-package-policies', + score: 1, + references: [], + version: '1.0.0', + attributes: { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: 'policy-id-1', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + }, + }, + ], + }); + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type: 'abcd', + references: [], + version: 'test', + attributes: {}, + }); + savedObjectsClient.update.mockImplementation( + async ( + type: string, + id: string, + attrs: any + ): Promise> => { + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type, + references: [], + version: 'test', + attributes: attrs, + }); + return attrs; + } + ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const result = await packagePolicyService.update( + savedObjectsClient, + elasticsearchClient, + 'the-package-policy-id', + { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: '93c46720-c217-11ea-9906-b5b8a21b268e', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + }, + { skipUniqueNameVerification: true } + ); + expect(result.name).toEqual('endpoint-1'); + }); + it('should throw if the user try to update input vars that are frozen', async () => { const savedObjectsClient = savedObjectsClientMock.create(); const mockPackagePolicy = createPackagePolicyMock(); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 7f294ca89d65..f52316dd4452 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -469,7 +469,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { esClient: ElasticsearchClient, id: string, packagePolicyUpdate: UpdatePackagePolicy, - options?: { user?: AuthenticatedUser; force?: boolean }, + options?: { user?: AuthenticatedUser; force?: boolean; skipUniqueNameVerification?: boolean }, currentVersion?: string ): Promise { const packagePolicy = { ...packagePolicyUpdate, name: packagePolicyUpdate.name.trim() }; @@ -479,22 +479,22 @@ class PackagePolicyClientImpl implements PackagePolicyClient { if (packagePolicyUpdate.is_managed && !options?.force) { throw new PackagePolicyRestrictionRelatedError(`Cannot update package policy ${id}`); } - if (!oldPackagePolicy) { throw new Error('Package policy not found'); } - // Check that the name does not exist already but exclude the current package policy - const existingPoliciesWithName = await this.list(soClient, { - perPage: SO_SEARCH_LIMIT, - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name:"${packagePolicy.name}"`, - }); - const filtered = (existingPoliciesWithName?.items || []).filter((p) => p.id !== id); - - if (filtered.length > 0) { - throw new FleetError( - `An integration policy with the name ${packagePolicy.name} already exists. Please rename it or choose a different name.` - ); + if (!options?.skipUniqueNameVerification) { + // Check that the name does not exist already but exclude the current package policy + const existingPoliciesWithName = await this.list(soClient, { + perPage: SO_SEARCH_LIMIT, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name:"${packagePolicy.name}"`, + }); + const filtered = (existingPoliciesWithName?.items || []).filter((p) => p.id !== id); + if (filtered.length > 0) { + throw new FleetError( + `An integration policy with the name ${packagePolicy.name} already exists. Please rename it or choose a different name.` + ); + } } let inputs = restOfPackagePolicy.inputs.map((input) => @@ -929,12 +929,17 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ); updatePackagePolicy.elasticsearch = packageInfo.elasticsearch; + const updateOptions = { + skipUniqueNameVerification: true, + ...options, + }; + await this.update( soClient, esClient, id, updatePackagePolicy, - options, + updateOptions, packagePolicy.package!.version ); diff --git a/x-pack/plugins/fleet/server/services/package_policy_service.ts b/x-pack/plugins/fleet/server/services/package_policy_service.ts index 4dd36a8c4fdf..0a16a01c3dc4 100644 --- a/x-pack/plugins/fleet/server/services/package_policy_service.ts +++ b/x-pack/plugins/fleet/server/services/package_policy_service.ts @@ -97,7 +97,7 @@ export interface PackagePolicyClient { esClient: ElasticsearchClient, id: string, packagePolicyUpdate: UpdatePackagePolicy, - options?: { user?: AuthenticatedUser; force?: boolean }, + options?: { user?: AuthenticatedUser; force?: boolean; skipUniqueNameVerification?: boolean }, currentVersion?: string ): Promise; From 5adfb8f0704d63ed9ea8bdf78ebff96a32791ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 27 Sep 2022 15:53:29 +0200 Subject: [PATCH 009/185] New extension point for multi step onboarding process. Also hides advanced options for endpoint package (#141748) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet/dev_docs/fleet_ui_extensions.md | 6 ++ .../components/page_steps/add_integration.tsx | 79 +++++++++++++------ x-pack/plugins/fleet/public/index.ts | 3 + .../fleet/public/types/ui_extensions.ts | 22 +++++- ...int_policy_create_multi_step_extension.tsx | 18 +++++ ...int_policy_create_multi_step_extension.tsx | 19 +++++ .../security_solution/public/plugin.tsx | 7 ++ 7 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension.tsx diff --git a/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md b/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md index 9dcb7112f7ef..46788ecac5da 100644 --- a/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md +++ b/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md @@ -33,6 +33,12 @@ export class Plugin { component: LazyEndpointPolicyCreateExtension, }); + registerExtension({ + package: 'endpoint', + view: 'package-policy-create-multi-step', + component: LazyEndpointPolicyCreateMultiStepExtension, + }); + registerExtension({ package: 'endpoint', view: 'package-detail-custom', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx index bce4d71b0425..c5ccd2916e46 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { safeLoad } from 'js-yaml'; @@ -19,9 +19,9 @@ import { isVerificationError } from '../../../../../../../../services'; import type { MultiPageStepLayoutProps } from '../../types'; import type { PackagePolicyFormState } from '../../../types'; import type { NewPackagePolicy } from '../../../../../../types'; -import { sendCreatePackagePolicy, useStartServices } from '../../../../../../hooks'; +import { sendCreatePackagePolicy, useStartServices, useUIExtension } from '../../../../../../hooks'; import type { RequestError } from '../../../../../../hooks'; -import { Error } from '../../../../../../components'; +import { Error, ExtensionWrapper } from '../../../../../../components'; import { sendGeneratePackagePolicy } from '../../hooks'; import { CreatePackagePolicyBottomBar, StandaloneModeWarningCallout } from '..'; import type { PackagePolicyValidationResults } from '../../../services'; @@ -212,6 +212,55 @@ export const AddIntegrationPageStep: React.FC = (props getBasePolicy(); }, []); // eslint-disable-line react-hooks/exhaustive-deps + const extensionView = useUIExtension(packageInfo.name ?? '', 'package-policy-create-multi-step'); + const addIntegrationExtensionView = useMemo(() => { + return ( + extensionView && ( + + + + ) + ); + }, [packagePolicy, extensionView]); + + const content = useMemo(() => { + if (packageInfo.name !== 'endpoint') { + return ( + <> + + + {validationResults && ( + + + + )} + + ); + } + }, [ + formState, + integrationInfo?.name, + packageInfo, + packagePolicy, + updatePackagePolicy, + validationResults, + ]); + if (!agentPolicy) { return ( = (props return ( <> {isManaged ? null : } - - - {validationResults && ( - - - - )} + {content} + {addIntegrationExtensionView} setIsManaged(true)} diff --git a/x-pack/plugins/fleet/public/index.ts b/x-pack/plugins/fleet/public/index.ts index a55937999c4d..823d46becb4f 100644 --- a/x-pack/plugins/fleet/public/index.ts +++ b/x-pack/plugins/fleet/public/index.ts @@ -35,6 +35,9 @@ export type { PackagePolicyCreateExtension, PackagePolicyCreateExtensionComponent, PackagePolicyCreateExtensionComponentProps, + PackagePolicyCreateMultiStepExtension, + PackagePolicyCreateMultiStepExtensionComponent, + PackagePolicyCreateMultiStepExtensionComponentProps, PackagePolicyEditExtension, PackagePolicyEditExtensionComponent, PackagePolicyEditExtensionComponentProps, diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index c3a9f05fc7d9..24f9fef73061 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -132,6 +132,25 @@ export interface PackagePolicyCreateExtension { Component: LazyExoticComponent; } +/** + * UI Component Extension is used on the pages displaying the ability to Create a multi step + * Integration Policy + */ +export type PackagePolicyCreateMultiStepExtensionComponent = + ComponentType; + +export interface PackagePolicyCreateMultiStepExtensionComponentProps { + /** The integration policy being created */ + newPolicy: NewPackagePolicy; +} + +/** Extension point registration contract for Integration Policy Create views in multi-step onboarding */ +export interface PackagePolicyCreateMultiStepExtension { + package: string; + view: 'package-policy-create-multi-step'; + Component: LazyExoticComponent; +} + /** * UI Component Extension is used to display a Custom tab (and view) under a given Integration */ @@ -178,4 +197,5 @@ export type UIExtensionPoint = | PackagePolicyCreateExtension | PackageAssetsExtension | PackageGenericErrorsListExtension - | AgentEnrollmentFlyoutFinalStepExtension; + | AgentEnrollmentFlyoutFinalStepExtension + | PackagePolicyCreateMultiStepExtension; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx new file mode 100644 index 000000000000..747d0990b8fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_multi_step_extension.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import type { PackagePolicyCreateMultiStepExtensionComponentProps } from '@kbn/fleet-plugin/public'; + +/** + * TBD: A component to be displayed in multi step onboarding process. + */ +export const EndpointPolicyCreateMultiStepExtension = + memo(({ newPolicy }) => { + return <>; + }); +EndpointPolicyCreateMultiStepExtension.displayName = 'EndpointPolicyCreateMultiStepExtension'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension.tsx new file mode 100644 index 000000000000..b989c07fef45 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { lazy } from 'react'; +import type { PackagePolicyCreateMultiStepExtensionComponent } from '@kbn/fleet-plugin/public'; + +export const LazyEndpointPolicyCreateMultiStepExtension = + lazy(async () => { + const { EndpointPolicyCreateMultiStepExtension } = await import( + './endpoint_policy_create_multi_step_extension' + ); + return { + default: EndpointPolicyCreateMultiStepExtension, + }; + }); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 7affdd066742..6ab6fcd6e628 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -60,6 +60,7 @@ import { ExperimentalFeaturesService } from './common/experimental_features_serv import { getLazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension'; import { LazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension'; +import { LazyEndpointPolicyCreateMultiStepExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_multi_step_extension'; import { getLazyEndpointPackageCustomExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_package_custom_extension'; import { getLazyEndpointPolicyResponseExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_response_extension'; import { getLazyEndpointGenericErrorsListExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_generic_errors_list'; @@ -246,6 +247,12 @@ export class Plugin implements IPlugin Date: Tue, 27 Sep 2022 09:55:44 -0400 Subject: [PATCH 010/185] change from host.os.name to host.os.type (#141432) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../infra/public/pages/metrics/hosts/components/hosts_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx index 9975de8db737..57b3ab0e683a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx @@ -64,7 +64,7 @@ const getLensHostsTable = ( dataType: 'string', operationType: 'terms', scale: 'ordinal', - sourceField: 'host.os.name', + sourceField: 'host.os.type', isBucketed: true, params: { size: 10000, From 9738b04524ab86be302256cdc7f2a420d98bec33 Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Tue, 27 Sep 2022 16:01:23 +0200 Subject: [PATCH 011/185] [Security Solution] Rule bulk schedule fixes and test coverage expansion (#141604) ## Summary Fixes issues, nits and [expands test coverage](https://docs.google.com/document/d/116x7ITTTJQ6cTiwaGK831_f6Ox7XB3qyLiHxC3Cmf8w/edit#) for PR: https://github.com/elastic/kibana/pull/140166 - Extends definition of `TimeUnit` type and its tests - Adds e2e test to test default values of Bulk Schedule flyout - Corrects copy as reported by @elastic/security-docs - Corrects validation for Interval field when editing rule schedule individually ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../src/time_duration/index.test.ts | 24 +++++++++++++++++- .../src/time_duration/index.ts | 6 ++++- .../e2e/detection_rules/bulk_edit_rules.cy.ts | 13 ++++++++++ .../cypress/screens/rules_bulk_edit.ts | 2 ++ .../cypress/tasks/rules_bulk_edit.ts | 25 +++++++++++++------ .../rules/step_schedule_rule/index.tsx | 1 + .../rules/all/bulk_actions/translations.tsx | 2 +- .../action_to_rules_client_operation.ts | 2 -- .../bulk_actions/rule_params_modifier.test.ts | 6 ++--- 9 files changed, 66 insertions(+), 15 deletions(-) diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts index 8af5f04bf16e..872affa9989a 100644 --- a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts +++ b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts @@ -11,7 +11,7 @@ import { left } from 'fp-ts/lib/Either'; import { TimeDuration } from '.'; import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -describe('time_unit', () => { +describe('TimeDuration', () => { test('it should validate a correctly formed TimeDuration with time unit of seconds', () => { const payload = '1s'; const decoded = TimeDuration.decode(payload); @@ -39,6 +39,17 @@ describe('time_unit', () => { expect(message.schema).toEqual(payload); }); + test('it should NOT validate a TimeDuration of 0 length', () => { + const payload = '0s'; + const decoded = TimeDuration.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + test('it should NOT validate a negative TimeDuration', () => { const payload = '-10s'; const decoded = TimeDuration.decode(payload); @@ -50,6 +61,17 @@ describe('time_unit', () => { expect(message.schema).toEqual({}); }); + test('it should NOT validate a decimal TimeDuration', () => { + const payload = '1.5s'; + const decoded = TimeDuration.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1.5s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + test('it should NOT validate a TimeDuration with some other time unit', () => { const payload = '10000000w'; const decoded = TimeDuration.decode(payload); diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts index c486e1d17168..5549979ac68d 100644 --- a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts +++ b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts @@ -21,8 +21,12 @@ export const TimeDuration = new t.Type( if (typeof input === 'string' && input.trim() !== '') { try { const inputLength = input.length; - const time = parseInt(input.trim().substring(0, inputLength - 1), 10); const unit = input.trim().at(-1); + const time = parseFloat(input.trim().substring(0, inputLength - 1)); + + if (!Number.isInteger(time)) { + return t.failure(input, context); + } if ( time >= 1 && Number.isSafeInteger(time) && diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts index 3cdb5920101d..745c542bf247 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts @@ -71,6 +71,7 @@ import { setScheduleIntervalTimeUnit, assertRuleScheduleValues, assertUpdateScheduleWarningExists, + assertDefaultValuesAreAppliedToScheduleFields, } from '../../tasks/rules_bulk_edit'; import { hasIndexPatterns, getDetails } from '../../tasks/rule_details'; @@ -493,6 +494,18 @@ describe('Detection rules, bulk edit', () => { }); describe('Schedule', () => { + it('Default values are applied to bulk edit schedule fields', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + clickUpdateScheduleMenuItem(); + + assertUpdateScheduleWarningExists(expectedNumberOfCustomRulesToBeEdited); + + assertDefaultValuesAreAppliedToScheduleFields({ + interval: 5, + lookback: 1, + }); + }); + it('Updates schedule for custom rules', () => { selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); clickUpdateScheduleMenuItem(); diff --git a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts index 341633380a3f..34e4f9515b27 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts @@ -56,4 +56,6 @@ export const UPDATE_SCHEDULE_INTERVAL_INPUT = export const UPDATE_SCHEDULE_LOOKBACK_INPUT = '[data-test-subj="bulkEditRulesScheduleLookbackSelector"]'; +export const UPDATE_SCHEDULE_TIME_INTERVAL = '[data-test-subj="interval"]'; + export const UPDATE_SCHEDULE_TIME_UNIT_SELECT = '[data-test-subj="timeType"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts index c3f9336e19fc..5b3ae403f4a0 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts @@ -193,6 +193,19 @@ export const typeScheduleLookback = (lookback: string) => { .blur(); }; +interface ScheduleFormFields { + interval: number; + lookback: number; +} + +export const assertDefaultValuesAreAppliedToScheduleFields = ({ + interval, + lookback, +}: ScheduleFormFields) => { + cy.get(UPDATE_SCHEDULE_INTERVAL_INPUT).find('input').should('have.value', interval); + cy.get(UPDATE_SCHEDULE_LOOKBACK_INPUT).find('input').should('have.value', lookback); +}; + type TimeUnit = 'Seconds' | 'Minutes' | 'Hours'; export const setScheduleIntervalTimeUnit = (timeUnit: TimeUnit) => { cy.get(UPDATE_SCHEDULE_INTERVAL_INPUT).within(() => { @@ -209,17 +222,15 @@ export const setScheduleLookbackTimeUnit = (timeUnit: TimeUnit) => { export const assertUpdateScheduleWarningExists = (expectedNumberOfNotMLRules: number) => { cy.get(RULES_BULK_EDIT_SCHEDULES_WARNING).should( 'have.text', - `You're about to apply changes to ${expectedNumberOfNotMLRules} selected rules. The changes you made will be overwritten to the existing Rule schedules and additional look-back time (if any).` + `You're about to apply changes to ${expectedNumberOfNotMLRules} selected rules. The changes you make will overwrite the existing rule schedules and additional look-back time (if any).` ); }; - -export const assertRuleScheduleValues = ({ - interval, - lookback, -}: { +interface RuleSchedule { interval: string; lookback: string; -}) => { +} + +export const assertRuleScheduleValues = ({ interval, lookback }: RuleSchedule) => { cy.get(SCHEDULE_DETAILS).within(() => { cy.get('dd').eq(0).should('contain.text', interval); cy.get('dd').eq(1).should('contain.text', lookback); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx index c42e5e4b4597..04ad3a490c89 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx @@ -95,6 +95,7 @@ const StepScheduleRuleComponent: FC = ({ idAria: 'detectionEngineStepScheduleRuleInterval', isDisabled: isLoading, dataTestSubj: 'detectionEngineStepScheduleRuleInterval', + minimumValue: 1, }} /> ( ), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts index c395798cdf61..550f624ed935 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/action_to_rules_client_operation.ts @@ -93,8 +93,6 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'schedule', operation: 'set', - // We need to pass a pure Interval object - // i.e. get rid of the meta property value: { interval: action.value.interval, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts index bfbad8ac2ece..5c194cc6c461 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts @@ -353,7 +353,7 @@ describe('ruleParamsModifier', () => { }); describe('schedule', () => { - test('should set timeline', () => { + test('should set schedule', () => { const INTERVAL_IN_MINUTES = 5; const LOOKBACK_IN_MINUTES = 1; const FROM_IN_SECONDS = (INTERVAL_IN_MINUTES + LOOKBACK_IN_MINUTES) * 60; @@ -367,8 +367,8 @@ describe('ruleParamsModifier', () => { }, ]); - // @ts-expect-error - expect(editedRuleParams.interval).toBeUndefined(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect((editedRuleParams as any).interval).toBeUndefined(); expect(editedRuleParams.meta).toStrictEqual({ from: '1m', }); From bde73c847d5aedfdf08f08c2e050c2faeff7633d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Alvarez=20Pi=C3=B1eiro?= <95703246+emilioalvap@users.noreply.github.com> Date: Tue, 27 Sep 2022 16:12:34 +0200 Subject: [PATCH 012/185] [Synthetics UI] Update synthetics-monitor saved object name normalizer (#139736) * Update synthetic-monitor so to be case-insensitive * Add e2e test checking for monitor sorting Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../journeys/monitor_management.journey.ts | 69 +++++++++++++++++++ .../lib/saved_objects/synthetics_monitor.ts | 1 + 2 files changed, 70 insertions(+) diff --git a/x-pack/plugins/synthetics/e2e/journeys/monitor_management.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/monitor_management.journey.ts index 2b0aeca66f06..c3b325183b05 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/monitor_management.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/monitor_management.journey.ts @@ -225,3 +225,72 @@ journey('Monitor Management breadcrumbs', async ({ page, params }: { page: Page; expect(isSuccessful).toBeTruthy(); }); }); + +journey( + 'MonitorManagement-case-insensitive sort', + async ({ page, params }: { page: Page; params: any }) => { + const uptime = monitorManagementPageProvider({ page, kibanaUrl: params.kibanaUrl }); + + const sortedMonitors = [ + Object.assign({}, configuration[DataStream.ICMP].monitorConfig, { + name: `A ${uuid.v4()}`, + }), + Object.assign({}, configuration[DataStream.ICMP].monitorConfig, { + name: `B ${uuid.v4()}`, + }), + Object.assign({}, configuration[DataStream.ICMP].monitorConfig, { + name: `aa ${uuid.v4()}`, + }), + ]; + + before(async () => { + await uptime.waitForLoadingToFinish(); + }); + + after(async () => { + await uptime.navigateToMonitorManagement(); + await uptime.deleteMonitors(); + await uptime.enableMonitorManagement(false); + }); + + step('Go to monitor-management', async () => { + await uptime.navigateToMonitorManagement(); + }); + + step('login to Kibana', async () => { + await uptime.loginToKibana(); + const invalid = await page.locator( + `text=Username or password is incorrect. Please try again.` + ); + expect(await invalid.isVisible()).toBeFalsy(); + }); + + for (const monitorConfig of sortedMonitors) { + step(`create monitor ${monitorConfig.name}`, async () => { + await uptime.enableMonitorManagement(); + await uptime.clickAddMonitor(); + await uptime.createMonitor({ monitorConfig, monitorType: DataStream.ICMP }); + const isSuccessful = await uptime.confirmAndSave(); + expect(isSuccessful).toBeTruthy(); + }); + } + + step(`list monitors in Monitor Management UI`, async () => { + await uptime.navigateToMonitorManagement(); + await Promise.all( + sortedMonitors.map((monitor) => + page.waitForSelector(`text=${monitor.name}`, { timeout: 160 * 1000 }) + ) + ); + + // Get first cell value from monitor table -> monitor name + const rows = page.locator('tbody tr td:first-child div.euiTableCellContent'); + expect(await rows.count()).toEqual(sortedMonitors.length); + + const expectedSort = sortedMonitors + .map((mn) => mn.name) + .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + expect(await rows.allTextContents()).toEqual(expectedSort); + }); + } +); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts index 61d0694d7cfa..52be898373ef 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts @@ -23,6 +23,7 @@ export const syntheticsMonitor: SavedObjectsType = { keyword: { type: 'keyword', ignore_above: 256, + normalizer: 'lowercase', }, }, }, From b854cd8f5156283acbca2aee5d9ec548553165bc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 23:54:44 +0930 Subject: [PATCH 013/185] Update dependency @types/node-forge to ^1.3.0 (main) (#141553) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 02511a00b56c..6c0913394e24 100644 --- a/package.json +++ b/package.json @@ -1177,7 +1177,7 @@ "@types/nock": "^10.0.3", "@types/node": "16.11.41", "@types/node-fetch": "^2.6.0", - "@types/node-forge": "^1.0.4", + "@types/node-forge": "^1.3.0", "@types/nodemailer": "^6.4.0", "@types/normalize-path": "^3.0.0", "@types/object-hash": "^1.3.0", diff --git a/yarn.lock b/yarn.lock index 0752e8a1611c..f809bc0cac46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8158,10 +8158,10 @@ "@types/node" "*" form-data "^2.3.3" -"@types/node-forge@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.0.4.tgz#82df44938848e111463080286876e4cbe2b297a0" - integrity sha512-UpX8LTRrarEZPQvQqF5/6KQAqZolOVckH7txWdlsWIJrhBFFtwEUTcqeDouhrJl6t0F7Wg5cyUOAqqF8a6hheg== +"@types/node-forge@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.0.tgz#c655e951e0fb5c4b53c9f4746c2128d4f93002fd" + integrity sha512-yUsIEHG3d81E2c+akGjZAMdVcjbfqMzpMjvpebnTO7pEGfqxCtBzpRV52kR1RETtwJ9fHkLdEjtaM+uMKWpwFA== dependencies: "@types/node" "*" From d5dd24124d40e898600ff839ce2f8a0650b53998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 27 Sep 2022 16:25:24 +0200 Subject: [PATCH 014/185] [Security Solution][Endpoint] Removes endpoint package from the excluded_packages list (#141783) * Removes endpoint package from the excluded_packages list * Fixes unit test --- .../screens/detail/utils/get_install_route_options.test.ts | 4 ++-- .../epm/screens/detail/utils/get_install_route_options.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts index 8d556434f882..e085b9034235 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.test.ts @@ -131,7 +131,7 @@ describe('getInstallPkgRouteOptions', () => { expect(getInstallPkgRouteOptions(opts)).toEqual(['fleet', expectedOptions]); }); - it('should not navigate to steps app for endpoint', () => { + it('should navigate to steps app for endpoint', () => { const opts = { currentPath: 'currentPath', integration: 'myintegration', @@ -144,7 +144,7 @@ describe('getInstallPkgRouteOptions', () => { const expectedRedirectURl = '/detail/endpoint-1.0.0/policies?integration=myintegration'; const expectedOptions = { - path: '/integrations/endpoint-1.0.0/add-integration/myintegration', + path: '/integrations/endpoint-1.0.0/add-integration/myintegration?useMultiPageLayout', state: { onCancelUrl: 'currentPath', onCancelNavigateTo: [ diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts index c4e6d7ffc4a8..6a8612a44f42 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/get_install_route_options.ts @@ -13,7 +13,6 @@ const EXCLUDED_PACKAGES = [ 'apm', 'cloud_security_posture', 'dga', - 'endpoint', 'fleet_server', 'kubernetes', 'osquery_manager', From 712f8801d6396448098248cf97dc671b12d7b174 Mon Sep 17 00:00:00 2001 From: Adam Demjen Date: Tue, 27 Sep 2022 10:37:34 -0400 Subject: [PATCH 015/185] [8.5] Change default name of destination field in ML Inference pipeline (#141819) * Use pipeline name as default destination field --- .../routes/enterprise_search/indices.ts | 2 +- .../create_ml_inference_pipeline.test.ts | 53 +++++++++++++------ .../utils/create_ml_inference_pipeline.ts | 9 ++-- .../utils/ml_inference_pipeline_utils.ts | 2 +- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts index fd4452ccb46a..db46da11f5f5 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts @@ -360,7 +360,7 @@ export function registerIndexRoutes({ pipelineName, modelId, sourceField, - destinationField || modelId, + destinationField, client.asCurrentUser ); } catch (error) { diff --git a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.test.ts b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.test.ts index c34bbae2f97f..d3aa24560594 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.test.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.test.ts @@ -16,6 +16,16 @@ import { getPrefixedInferencePipelineProcessorName, } from './ml_inference_pipeline_utils'; +const mockClient = { + ingest: { + getPipeline: jest.fn(), + putPipeline: jest.fn(), + }, + ml: { + getTrainedModels: jest.fn(), + }, +}; + describe('createMlInferencePipeline util function', () => { const pipelineName = 'my-pipeline'; const modelId = 'my-model-id'; @@ -23,16 +33,6 @@ describe('createMlInferencePipeline util function', () => { const destinationField = 'my-dest-field'; const inferencePipelineGeneratedName = getPrefixedInferencePipelineProcessorName(pipelineName); - const mockClient = { - ingest: { - getPipeline: jest.fn(), - putPipeline: jest.fn(), - }, - ml: { - getTrainedModels: jest.fn(), - }, - }; - mockClient.ml.getTrainedModels.mockImplementation(() => Promise.resolve({ trained_model_configs: [ @@ -86,6 +86,32 @@ describe('createMlInferencePipeline util function', () => { ); }); + it('should default the destination field to the pipeline name', async () => { + mockClient.ingest.getPipeline.mockImplementation(() => Promise.reject({ statusCode: 404 })); // Pipeline does not exist + mockClient.ingest.putPipeline.mockImplementation(() => Promise.resolve({ acknowledged: true })); + + await createMlInferencePipeline( + pipelineName, + modelId, + sourceField, + undefined, // Omitted destination field + mockClient as unknown as ElasticsearchClient + ); + + // Verify the object passed to pipeline creation contains the default target field name + expect(mockClient.ingest.putPipeline).toHaveBeenCalledWith( + expect.objectContaining({ + processors: expect.arrayContaining([ + expect.objectContaining({ + inference: expect.objectContaining({ + target_field: `ml.inference.${pipelineName}`, + }), + }), + ]), + }) + ); + }); + it('should throw an error without creating the pipeline if it already exists', () => { mockClient.ingest.getPipeline.mockImplementation(() => Promise.resolve({ @@ -111,13 +137,6 @@ describe('addSubPipelineToIndexSpecificMlPipeline util function', () => { const parentPipelineId = getInferencePipelineNameFromIndexName(indexName); const pipelineName = 'ml-inference-my-pipeline'; - const mockClient = { - ingest: { - getPipeline: jest.fn(), - putPipeline: jest.fn(), - }, - }; - beforeEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts index ebe69f98118d..dfcd8d488497 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts @@ -15,6 +15,7 @@ import { formatMlPipelineBody } from '../lib/pipelines/create_pipeline_definitio import { getInferencePipelineNameFromIndexName, getPrefixedInferencePipelineProcessorName, + formatPipelineName, } from './ml_inference_pipeline_utils'; /** @@ -41,14 +42,14 @@ export const createAndReferenceMlInferencePipeline = async ( pipelineName: string, modelId: string, sourceField: string, - destinationField: string, + destinationField: string | null | undefined, esClient: ElasticsearchClient ): Promise => { const createPipelineResult = await createMlInferencePipeline( pipelineName, modelId, sourceField, - destinationField || modelId, + destinationField, esClient ); @@ -76,7 +77,7 @@ export const createMlInferencePipeline = async ( pipelineName: string, modelId: string, sourceField: string, - destinationField: string, + destinationField: string | null | undefined, esClient: ElasticsearchClient ): Promise => { const inferencePipelineGeneratedName = getPrefixedInferencePipelineProcessorName(pipelineName); @@ -99,7 +100,7 @@ export const createMlInferencePipeline = async ( inferencePipelineGeneratedName, modelId, sourceField, - destinationField, + destinationField || formatPipelineName(pipelineName), esClient ); diff --git a/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts b/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts index e059f5b9090c..6547034f2540 100644 --- a/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts +++ b/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts @@ -13,7 +13,7 @@ export const getPrefixedInferencePipelineProcessorName = (pipelineName: string) ? formatPipelineName(pipelineName) : `ml-inference-${formatPipelineName(pipelineName)}`; -const formatPipelineName = (rawName: string) => +export const formatPipelineName = (rawName: string) => rawName .trim() .replace(/\s+/g, '_') // Convert whitespaces to underscores From d41e5ab12724f5dacbc91c416a6a0422e4b39b79 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 27 Sep 2022 10:44:04 -0400 Subject: [PATCH 016/185] [Response Ops] [Connectors] Move connectors type UI components to `stack_connectors` plugin (#140444) * Initial commit of stack connectors plugin * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixing up xmatters. Moving library functions around * [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs' * Fixing up webhook * Fixing up teams and slack * Fixing up index, email, pagerduty, server log * Fixing i18n * wip * Moving well know email route to stack connectors plugin * Fixing types * Fixing unit tests * Adding index.ts * Cleanup * Updating READMEs * [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs' * Moving to domain specific folders and updating codeowners * Fixing codeowners * Fixing types * Initial commit moving client code to stack connectors plugin * Updated all imports * Updating email * Updating index * Updating stack connectors * Updating cases connectors * Cleanup * Splitting test_utils * Fixing all imports * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixing types and limits * Fixing unit tests * Fixing CI * Updating codeowners Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .eslintrc.js | 4 +- .github/CODEOWNERS | 2 + packages/kbn-optimizer/limits.yml | 1 + .../plugins/stack_connectors/jest.config.js | 2 +- x-pack/plugins/stack_connectors/kibana.json | 4 +- .../cases}/cases_webhook/action_variables.ts | 0 .../cases/cases_webhook/index.ts | 8 + .../cases}/cases_webhook/steps/auth.tsx | 2 +- .../cases}/cases_webhook/steps/create.tsx | 2 +- .../cases}/cases_webhook/steps/get.tsx | 2 +- .../cases}/cases_webhook/steps/index.ts | 0 .../cases_webhook/steps/update.styles.ts | 0 .../cases}/cases_webhook/steps/update.tsx | 3 +- .../cases/cases_webhook/translations.ts | 477 +++++++++++ .../cases}/cases_webhook/types.ts | 4 +- .../cases}/cases_webhook/validator.ts | 2 +- .../cases/cases_webhook/webhook.test.tsx | 54 ++ .../cases}/cases_webhook/webhook.tsx | 11 +- .../cases_webhook/webhook_connectors.test.tsx | 10 +- .../cases_webhook/webhook_connectors.tsx | 4 +- .../cases_webhook/webhook_params.test.tsx | 4 +- .../cases}/cases_webhook/webhook_params.tsx | 34 +- .../public/connector_types/cases/index.ts | 17 + .../connector_types/cases}/jira/api.test.ts | 0 .../public/connector_types/cases}/jira/api.ts | 8 +- .../connector_types/cases/jira}/index.ts | 2 +- .../connector_types/cases}/jira/jira.test.tsx | 32 +- .../connector_types/cases}/jira/jira.tsx | 18 +- .../cases}/jira/jira_connectors.test.tsx | 4 +- .../cases}/jira/jira_connectors.tsx | 12 +- .../cases}/jira/jira_params.test.tsx | 4 +- .../cases}/jira/jira_params.tsx | 39 +- .../connector_types/cases}/jira/logo.tsx | 2 +- .../cases}/jira/search_issues.tsx | 2 +- .../cases}/jira/translations.ts | 35 +- .../connector_types/cases}/jira/types.ts | 4 +- .../jira/use_get_fields_by_issue_type.tsx | 2 +- .../cases}/jira/use_get_issue_types.tsx | 2 +- .../cases}/jira/use_get_issues.tsx | 2 +- .../cases}/jira/use_get_single_issue.tsx | 2 +- .../cases}/resilient/api.test.ts | 0 .../connector_types/cases}/resilient/api.ts | 7 +- .../connector_types/cases/resilient/index.ts | 8 + .../connector_types/cases}/resilient/logo.tsx | 2 +- .../cases/resilient/resilient.test.tsx | 53 ++ .../cases}/resilient/resilient.tsx | 18 +- .../resilient/resilient_connectors.test.tsx | 4 +- .../cases}/resilient/resilient_connectors.tsx | 11 +- .../resilient/resilient_params.test.tsx | 2 +- .../cases}/resilient/resilient_params.tsx | 31 +- .../cases}/resilient/translations.ts | 21 +- .../connector_types/cases}/resilient/types.ts | 4 +- .../resilient/use_get_incident_types.tsx | 2 +- .../cases}/resilient/use_get_severity.tsx | 2 +- .../cases}/servicenow/api.test.ts | 0 .../connector_types/cases}/servicenow/api.ts | 9 +- .../application_required_callout.test.tsx | 0 .../application_required_callout.tsx | 6 +- .../auth_types/credentials_auth.tsx | 2 +- .../cases}/servicenow/auth_types/index.ts | 0 .../cases}/servicenow/auth_types/oauth.tsx | 2 +- .../cases}/servicenow/credentials.test.tsx | 4 +- .../cases}/servicenow/credentials.tsx | 0 .../cases}/servicenow/credentials_api_url.tsx | 4 +- .../servicenow/deprecated_callout.test.tsx | 0 .../cases}/servicenow/deprecated_callout.tsx | 8 +- .../cases}/servicenow/helpers.test.ts | 2 +- .../cases}/servicenow/helpers.ts | 7 +- .../cases}/servicenow/index.ts | 6 +- .../servicenow/installation_callout.test.tsx | 0 .../servicenow/installation_callout.tsx | 0 .../cases}/servicenow/logo.tsx | 2 +- .../cases/servicenow/servicenow.test.tsx | 80 ++ .../cases}/servicenow/servicenow.tsx | 23 +- .../servicenow/servicenow_connectors.test.tsx | 12 +- .../servicenow/servicenow_connectors.tsx | 15 +- .../servicenow_connectors_no_app.test.tsx | 6 +- .../servicenow_connectors_no_app.tsx | 5 +- .../servicenow_itom_params.test.tsx | 4 +- .../servicenow/servicenow_itom_params.tsx | 10 +- .../servicenow_itsm_params.test.tsx | 4 +- .../servicenow/servicenow_itsm_params.tsx | 13 +- .../servicenow/servicenow_selection_row.tsx | 6 +- .../servicenow/servicenow_sir_params.test.tsx | 4 +- .../servicenow/servicenow_sir_params.tsx | 13 +- .../servicenow/sn_store_button.test.tsx | 0 .../cases}/servicenow/sn_store_button.tsx | 0 .../cases}/servicenow/translations.ts | 145 ++-- .../cases}/servicenow/types.ts | 4 +- .../servicenow/update_connector.test.tsx | 2 +- .../cases}/servicenow/update_connector.tsx | 25 +- .../cases}/servicenow/use_choices.test.tsx | 6 +- .../cases}/servicenow/use_choices.tsx | 2 +- .../servicenow/use_get_app_info.test.tsx | 2 +- .../cases}/servicenow/use_get_app_info.tsx | 0 .../servicenow/use_get_choices.test.tsx | 6 +- .../cases}/servicenow/use_get_choices.tsx | 2 +- .../cases}/swimlane/api.test.ts | 0 .../connector_types/cases}/swimlane/api.ts | 0 .../cases}/swimlane/helpers.ts | 0 .../connector_types/cases/swimlane/index.ts | 8 + .../connector_types/cases}/swimlane/logo.tsx | 2 +- .../connector_types/cases}/swimlane/mocks.ts | 0 .../cases}/swimlane/steps/index.ts | 0 .../swimlane/steps/swimlane_connection.tsx | 5 +- .../cases}/swimlane/steps/swimlane_fields.tsx | 2 +- .../cases}/swimlane/swimlane.test.tsx | 32 +- .../cases}/swimlane/swimlane.tsx | 11 +- .../swimlane/swimlane_connectors.test.tsx | 4 +- .../cases}/swimlane/swimlane_connectors.tsx | 4 +- .../cases}/swimlane/swimlane_params.test.tsx | 0 .../cases}/swimlane/swimlane_params.tsx | 8 +- .../cases}/swimlane/translations.ts | 81 +- .../connector_types/cases}/swimlane/types.ts | 4 +- .../swimlane/use_get_application.test.tsx | 4 +- .../cases}/swimlane/use_get_application.tsx | 0 .../connector_types/cases/xmatters/index.ts | 8 + .../connector_types/cases}/xmatters/logo.tsx | 2 +- .../cases}/xmatters/translations.ts | 27 +- .../cases/xmatters/xmatters.test.tsx | 48 ++ .../cases}/xmatters/xmatters.tsx | 22 +- .../xmatters/xmatters_connectors.test.tsx | 2 +- .../cases}/xmatters/xmatters_connectors.tsx | 18 +- .../cases}/xmatters/xmatters_params.test.tsx | 2 +- .../cases}/xmatters/xmatters_params.tsx | 41 +- .../public/connector_types/index.ts | 60 ++ .../lib}/extract_action_variable.ts | 0 .../lib}/rewrite_response_body.ts | 0 .../connector_types/lib}/test_utils.tsx | 23 +- .../public/connector_types/security}/index.ts | 2 - .../connector_types/stack}/email/api.ts | 4 +- .../stack}/email/email.test.tsx | 30 +- .../connector_types/stack}/email/email.tsx | 85 +- .../stack}/email/email_connector.test.tsx | 6 +- .../stack}/email/email_connector.tsx | 16 +- .../stack}/email/email_params.test.tsx | 0 .../stack}/email/email_params.tsx | 38 +- .../stack}/email/exchange_form.test.tsx | 4 +- .../stack}/email/exchange_form.tsx | 9 +- .../connector_types/stack/email}/index.ts | 2 +- .../stack}/email/translations.ts | 57 +- .../stack}/email/use_email_config.test.ts | 0 .../stack}/email/use_email_config.ts | 6 +- .../stack}/es_index/es_index.test.tsx | 40 +- .../stack}/es_index/es_index.tsx | 39 +- .../es_index/es_index_connector.test.tsx | 18 +- .../stack}/es_index/es_index_connector.tsx | 28 +- .../stack}/es_index/es_index_params.test.tsx | 8 +- .../stack}/es_index/es_index_params.tsx | 38 +- .../connector_types/stack/es_index}/index.ts | 2 +- .../stack}/es_index/translations.ts | 14 +- .../public/connector_types/stack/index.ts | 14 + .../connector_types/stack/pagerduty/index.ts | 8 + .../connector_types/stack}/pagerduty/logo.tsx | 2 +- .../stack}/pagerduty/pagerduty.test.tsx | 32 +- .../stack}/pagerduty/pagerduty.tsx | 45 +- .../pagerduty/pagerduty_connectors.test.tsx | 4 +- .../stack}/pagerduty/pagerduty_connectors.tsx | 6 +- .../pagerduty/pagerduty_params.test.tsx | 2 +- .../stack}/pagerduty/pagerduty_params.tsx | 54 +- .../stack}/pagerduty/translations.ts | 12 +- .../connector_types/stack/server_log/index.ts | 8 + .../stack/server_log/server_log.test.tsx | 55 ++ .../stack}/server_log/server_log.tsx | 22 +- .../server_log/server_log_params.test.tsx | 2 +- .../stack}/server_log/server_log_params.tsx | 24 +- .../connector_types/stack/slack}/index.ts | 2 +- .../stack/slack/slack.test.tsx | 54 ++ .../connector_types/stack}/slack/slack.tsx | 27 +- .../stack}/slack/slack_connectors.test.tsx | 4 +- .../stack}/slack/slack_connectors.tsx | 7 +- .../stack}/slack/slack_params.test.tsx | 0 .../stack}/slack/slack_params.tsx | 15 +- .../stack}/slack/translations.ts | 6 +- .../connector_types/stack/teams}/index.ts | 2 +- .../connector_types/stack}/teams/logo.tsx | 2 +- .../stack/teams/teams.test.tsx | 53 ++ .../connector_types/stack}/teams/teams.tsx | 27 +- .../stack}/teams/teams_connectors.test.tsx | 4 +- .../stack}/teams/teams_connectors.tsx | 6 +- .../stack}/teams/teams_params.test.tsx | 2 +- .../stack}/teams/teams_params.tsx | 15 +- .../stack}/teams/translations.ts | 6 +- .../connector_types/stack/webhook/index.ts | 8 + .../stack}/webhook/translations.ts | 28 +- .../stack/webhook/webhook.test.tsx | 54 ++ .../stack}/webhook/webhook.tsx | 27 +- .../webhook/webhook_connectors.test.tsx | 2 +- .../stack}/webhook/webhook_connectors.tsx | 6 +- .../stack}/webhook/webhook_params.test.tsx | 4 +- .../stack}/webhook/webhook_params.tsx | 17 +- .../public/connector_types}/types.ts | 2 +- .../plugins/stack_connectors/public/index.ts | 11 + .../plugins/stack_connectors/public/mocks.ts | 15 + .../plugins/stack_connectors/public/plugin.ts | 35 + x-pack/plugins/stack_connectors/tsconfig.json | 6 +- .../translations/translations/fr-FR.json | 755 +++++++++-------- .../translations/translations/ja-JP.json | 759 +++++++++--------- .../translations/translations/zh-CN.json | 758 +++++++++-------- x-pack/plugins/triggers_actions_ui/README.md | 4 +- .../cases_webhook/translations.ts | 510 ------------ .../cases_webhook/webhook.test.tsx | 54 -- .../components/builtin_action_types/index.ts | 58 -- .../builtin_action_types/pagerduty/index.ts | 8 - .../builtin_action_types/resilient/index.ts | 8 - .../resilient/resilient.test.tsx | 53 -- .../builtin_action_types/server_log/index.ts | 8 - .../server_log/server_log.test.tsx | 55 -- .../servicenow/servicenow.test.tsx | 80 -- .../builtin_action_types/slack/slack.test.tsx | 54 -- .../builtin_action_types/swimlane/index.ts | 8 - .../builtin_action_types/teams/index.ts | 8 - .../builtin_action_types/teams/teams.test.tsx | 53 -- .../webhook/webhook.test.tsx | 54 -- .../builtin_action_types/xmatters/index.ts | 8 - .../xmatters/xmatters.test.tsx | 48 -- .../public/application/components/index.ts | 17 + .../components/simple_connector_form.test.tsx | 2 +- .../application/components/test_utils.tsx | 86 ++ .../public/application/constants/index.ts | 1 - .../public/application/lib/index.ts | 3 + .../connector_form.test.tsx | 5 +- .../connector_form_fields.test.tsx | 2 +- .../connector_form_fields_global.test.tsx | 2 +- .../create_connector_flyout/index.test.tsx | 5 +- .../edit_connector_flyout/index.test.tsx | 5 +- .../encrypted_fields_callout.test.tsx | 2 +- .../sections/action_connector_form/index.ts | 2 + .../public/common/index.ts | 3 +- .../public/common/lib/index.ts | 1 + .../triggers_actions_ui/public/index.ts | 42 + .../triggers_actions_ui/public/mocks.ts | 8 - .../triggers_actions_ui/public/plugin.ts | 8 - .../plugins/triggers_actions_ui/tsconfig.json | 1 - 234 files changed, 3322 insertions(+), 3240 deletions(-) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/action_variables.ts (100%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/auth.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/create.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/get.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/index.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/update.styles.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/steps/update.tsx (97%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/translations.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/types.ts (82%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/validator.ts (98%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/webhook.tsx (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/webhook_connectors.test.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/webhook_connectors.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/webhook_params.test.tsx (93%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/cases_webhook/webhook_params.tsx (84%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/api.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/api.ts (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/webhook => stack_connectors/public/connector_types/cases/jira}/index.ts (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira.test.tsx (57%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira.tsx (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira_connectors.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira_connectors.tsx (86%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira_params.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/jira_params.tsx (90%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/logo.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/search_issues.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/translations.ts (55%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/types.ts (81%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/use_get_fields_by_issue_type.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/use_get_issue_types.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/use_get_issues.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/jira/use_get_single_issue.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/api.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/api.ts (88%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/logo.tsx (98%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/resilient.tsx (77%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/resilient_connectors.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/resilient_connectors.tsx (86%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/resilient_params.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/resilient_params.tsx (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/translations.ts (55%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/types.ts (75%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/use_get_incident_types.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/resilient/use_get_severity.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/api.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/api.ts (92%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/application_required_callout.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/application_required_callout.tsx (81%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/auth_types/credentials_auth.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/auth_types/index.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/auth_types/oauth.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/credentials.test.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/credentials.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/credentials_api_url.tsx (92%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/deprecated_callout.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/deprecated_callout.tsx (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/helpers.test.ts (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/helpers.ts (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/index.ts (73%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/installation_callout.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/installation_callout.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/logo.tsx (97%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow.tsx (84%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_connectors.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_connectors.tsx (92%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_connectors_no_app.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_connectors_no_app.tsx (85%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_itom_params.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_itom_params.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_itsm_params.test.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_itsm_params.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_selection_row.tsx (81%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_sir_params.test.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/servicenow_sir_params.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/sn_store_button.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/sn_store_button.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/translations.ts (50%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/types.ts (92%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/update_connector.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/update_connector.tsx (86%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_choices.test.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_choices.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_get_app_info.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_get_app_info.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_get_choices.test.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/servicenow/use_get_choices.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/api.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/api.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/helpers.ts (100%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/logo.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/mocks.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/steps/index.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/steps/swimlane_connection.tsx (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/steps/swimlane_fields.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane.test.tsx (55%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane.tsx (85%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane_connectors.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane_connectors.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane_params.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/swimlane_params.tsx (95%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/translations.ts (53%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/types.ts (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/use_get_application.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/swimlane/use_get_application.tsx (100%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/logo.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/translations.ts (54%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/xmatters.tsx (75%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/xmatters_connectors.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/xmatters_connectors.tsx (89%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/xmatters_params.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/cases}/xmatters/xmatters_params.tsx (68%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/lib}/extract_action_variable.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/lib}/rewrite_response_body.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/lib}/test_utils.tsx (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/jira => stack_connectors/public/connector_types/security}/index.ts (80%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/api.ts (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email.test.tsx (74%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email.tsx (67%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email_connector.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email_connector.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email_params.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/email_params.tsx (85%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/exchange_form.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/exchange_form.tsx (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/slack => stack_connectors/public/connector_types/stack/email}/index.ts (78%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/translations.ts (54%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/use_email_config.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/email/use_email_config.ts (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index.test.tsx (61%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index.tsx (59%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index_connector.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index_connector.tsx (87%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index_params.test.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/es_index_params.tsx (84%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook => stack_connectors/public/connector_types/stack/es_index}/index.ts (77%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/es_index/translations.ts (64%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/logo.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty.test.tsx (57%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty.tsx (71%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty_connectors.test.tsx (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty_connectors.tsx (91%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty_params.test.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/pagerduty_params.tsx (76%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/pagerduty/translations.ts (62%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/index.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/server_log/server_log.tsx (63%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/server_log/server_log_params.test.tsx (98%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/server_log/server_log_params.tsx (79%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/email => stack_connectors/public/connector_types/stack/slack}/index.ts (78%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/slack.tsx (59%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/slack_connectors.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/slack_connectors.tsx (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/slack_params.test.tsx (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/slack_params.tsx (79%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/slack/translations.ts (67%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types/es_index => stack_connectors/public/connector_types/stack/teams}/index.ts (78%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/logo.tsx (99%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/teams.tsx (59%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/teams_connectors.test.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/teams_connectors.tsx (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/teams_params.test.tsx (93%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/teams_params.tsx (74%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/teams/translations.ts (67%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/index.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/translations.ts (55%) create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.test.tsx rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/webhook.tsx (67%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/webhook_connectors.test.tsx (99%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/webhook_connectors.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/webhook_params.test.tsx (88%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types/stack}/webhook/webhook_params.tsx (69%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_action_types => stack_connectors/public/connector_types}/types.ts (97%) create mode 100644 x-pack/plugins/stack_connectors/public/index.ts create mode 100644 x-pack/plugins/stack_connectors/public/mocks.ts create mode 100644 x-pack/plugins/stack_connectors/public/plugin.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/translations.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/test_utils.tsx diff --git a/.eslintrc.js b/.eslintrc.js index df107348cfaf..902643dbe506 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1314,7 +1314,7 @@ module.exports = { { // typescript for front and back end files: [ - 'x-pack/plugins/{alerting,stack_alerts,stack_connectors,actions,task_manager,event_log}/**/*.{ts,tsx}', + 'x-pack/plugins/{alerting,stack_alerts,actions,task_manager,event_log}/**/*.{ts,tsx}', ], rules: { '@typescript-eslint/no-explicit-any': 'error', @@ -1322,7 +1322,7 @@ module.exports = { }, { // typescript only for back end - files: ['x-pack/plugins/triggers_actions_ui/server/**/*.ts'], + files: ['x-pack/plugins/{stack_connectors,triggers_actions_ui}/server/**/*.ts'], rules: { '@typescript-eslint/no-explicit-any': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 63c5c8047c6b..d0c6e476d6d4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -331,7 +331,9 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/plugins/event_log/ @elastic/response-ops /x-pack/plugins/task_manager/ @elastic/response-ops /x-pack/plugins/stack_connectors/ @elastic/response-ops +/x-pack/plugins/stack_connectors/public/connector_types/stack/ @elastic/response-ops-execution /x-pack/plugins/stack_connectors/server/connector_types/stack/ @elastic/response-ops-execution +/x-pack/plugins/stack_connectors/public/connector_types/cases/ @elastic/response-ops-cases /x-pack/plugins/stack_connectors/server/connector_types/cases/ @elastic/response-ops-cases /x-pack/test/alerting_api_integration/ @elastic/response-ops /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/response-ops diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index c8daaf258deb..440ff3ce2b12 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -108,6 +108,7 @@ pageLoadAssetSize: snapshotRestore: 79032 spaces: 57868 stackAlerts: 29684 + stackConnectors: 36314 synthetics: 40958 telemetry: 51957 telemetryManagementSection: 38586 diff --git a/x-pack/plugins/stack_connectors/jest.config.js b/x-pack/plugins/stack_connectors/jest.config.js index 9a343089b947..6bfda7da5ca5 100644 --- a/x-pack/plugins/stack_connectors/jest.config.js +++ b/x-pack/plugins/stack_connectors/jest.config.js @@ -12,6 +12,6 @@ module.exports = { coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/stack_connectors', coverageReporters: ['text', 'html'], collectCoverageFrom: [ - '/x-pack/plugins/stack_connectors/{common,server}/**/*.{js,ts,tsx}', + '/x-pack/plugins/stack_connectors/{common,public,server}/**/*.{js,ts,tsx}', ], }; diff --git a/x-pack/plugins/stack_connectors/kibana.json b/x-pack/plugins/stack_connectors/kibana.json index a2fc97ad9547..fc55b723e5c5 100644 --- a/x-pack/plugins/stack_connectors/kibana.json +++ b/x-pack/plugins/stack_connectors/kibana.json @@ -8,6 +8,6 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "stack_connectors"], - "requiredPlugins": ["actions"], - "ui": false + "requiredPlugins": ["actions", "esUiShared", "triggersActionsUi"], + "ui": true } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/action_variables.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/action_variables.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/action_variables.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/action_variables.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/index.ts new file mode 100644 index 000000000000..88107a4f6d51 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getCasesWebhookConnectorType } from './webhook'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/auth.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/auth.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/auth.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/auth.tsx index b356fb716d06..85e0f5e9e31b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/auth.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/auth.tsx @@ -23,7 +23,7 @@ import { } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field, TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; -import { PasswordField } from '../../../password_field'; +import { PasswordField } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from '../translations'; const { emptyField } = fieldValidators; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/create.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/create.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/create.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/create.tsx index c754a89ed89f..ffa72576feb6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/create.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/create.tsx @@ -10,8 +10,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import { FIELD_TYPES, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { JsonFieldWrapper } from '@kbn/triggers-actions-ui-plugin/public'; import { containsTitleAndDesc } from '../validator'; -import { JsonFieldWrapper } from '../../../json_field_wrapper'; import { casesVars } from '../action_variables'; import { HTTP_VERBS } from '../webhook_connectors'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/get.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/get.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/get.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/get.tsx index 3f0cf52dcfcb..e8f233408a4c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/get.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/get.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; -import { MustacheTextFieldWrapper } from '../../../mustache_text_field_wrapper'; +import { MustacheTextFieldWrapper } from '@kbn/triggers-actions-ui-plugin/public'; import { containsExternalId, containsExternalIdOrTitle } from '../validator'; import { urlVars, urlVarsExt } from '../action_variables'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/index.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/index.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/update.styles.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/update.styles.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/update.styles.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/update.styles.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/update.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/update.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/update.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/update.tsx index e8305e743977..a140ecbc6a22 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/steps/update.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/steps/update.tsx @@ -10,10 +10,9 @@ import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import { FIELD_TYPES, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { JsonFieldWrapper, MustacheTextFieldWrapper } from '@kbn/triggers-actions-ui-plugin/public'; import { containsCommentsOrEmpty, containsTitleAndDesc, isUrlButCanBeEmpty } from '../validator'; -import { MustacheTextFieldWrapper } from '../../../mustache_text_field_wrapper'; import { casesVars, commentVars, urlVars } from '../action_variables'; -import { JsonFieldWrapper } from '../../../json_field_wrapper'; import { HTTP_VERBS } from '../webhook_connectors'; import { styles } from './update.styles'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/translations.ts new file mode 100644 index 000000000000..1ef221566352 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/translations.ts @@ -0,0 +1,477 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 CREATE_URL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateUrlText', + { + defaultMessage: 'Create case URL is required.', + } +); +export const CREATE_INCIDENT_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentText', + { + defaultMessage: 'Create case object is required and must be valid JSON.', + } +); + +export const CREATE_METHOD_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateMethodText', + { + defaultMessage: 'Create case method is required.', + } +); + +export const CREATE_RESPONSE_KEY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentResponseKeyText', + { + defaultMessage: 'Create case response case id key is required.', + } +); + +export const UPDATE_URL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredUpdateUrlText', + { + defaultMessage: 'Update case URL is required.', + } +); +export const UPDATE_INCIDENT_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredUpdateIncidentText', + { + defaultMessage: 'Update case object is required and must be valid JSON.', + } +); + +export const UPDATE_METHOD_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredUpdateMethodText', + { + defaultMessage: 'Update case method is required.', + } +); + +export const CREATE_COMMENT_URL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentUrlText', + { + defaultMessage: 'Create comment URL must be URL format.', + } +); +export const CREATE_COMMENT_MESSAGE = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentIncidentText', + { + defaultMessage: 'Create comment object must be valid JSON.', + } +); + +export const CREATE_COMMENT_METHOD_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentMethodText', + { + defaultMessage: 'Create comment method is required.', + } +); + +export const GET_INCIDENT_URL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentUrlText', + { + defaultMessage: 'Get case URL is required.', + } +); +export const GET_RESPONSE_EXTERNAL_TITLE_KEY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseExternalTitleKeyText', + { + defaultMessage: 'Get case response external case title key is re quired.', + } +); +export const GET_RESPONSE_EXTERNAL_CREATED_KEY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseCreatedKeyText', + { + defaultMessage: 'Get case response created date key is required.', + } +); +export const GET_RESPONSE_EXTERNAL_UPDATED_KEY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseUpdatedKeyText', + { + defaultMessage: 'Get case response updated date key is required.', + } +); +export const GET_INCIDENT_VIEW_URL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentViewUrlKeyText', + { + defaultMessage: 'View case URL is required.', + } +); + +export const MISSING_VARIABLES = (variables: string[]) => + i18n.translate('xpack.stackConnectors.components.casesWebhook.error.missingVariables', { + defaultMessage: + 'Missing required {variableCount, plural, one {variable} other {variables}}: {variables}', + values: { variableCount: variables.length, variables: variables.join(', ') }, + }); + +export const USERNAME_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredAuthUserNameText', + { + defaultMessage: 'Username is required.', + } +); + +export const SUMMARY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.error.requiredWebhookSummaryText', + { + defaultMessage: 'Title is required.', + } +); + +export const KEY_LABEL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.keyTextFieldLabel', + { + defaultMessage: 'Key', + } +); + +export const VALUE_LABEL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.valueTextFieldLabel', + { + defaultMessage: 'Value', + } +); + +export const ADD_BUTTON = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.addHeaderButton', + { + defaultMessage: 'Add', + } +); + +export const DELETE_BUTTON = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.deleteHeaderButton', + { + defaultMessage: 'Delete', + description: 'Delete HTTP header', + } +); + +export const CREATE_INCIDENT_METHOD = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentMethodTextFieldLabel', + { + defaultMessage: 'Create Case Method', + } +); + +export const CREATE_INCIDENT_URL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentUrlTextFieldLabel', + { + defaultMessage: 'Create Case URL', + } +); + +export const CREATE_INCIDENT_JSON = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentJsonTextFieldLabel', + { + defaultMessage: 'Create Case Object', + } +); + +export const CREATE_INCIDENT_JSON_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentJsonHelpText', + { + defaultMessage: + 'JSON object to create case. Use the variable selector to add Cases data to the payload.', + } +); + +export const JSON = i18n.translate('xpack.stackConnectors.components.casesWebhook.jsonFieldLabel', { + defaultMessage: 'JSON', +}); +export const CODE_EDITOR = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.jsonCodeEditorAriaLabel', + { + defaultMessage: 'Code editor', + } +); + +export const CREATE_INCIDENT_RESPONSE_KEY = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyTextFieldLabel', + { + defaultMessage: 'Create Case Response Case Key', + } +); + +export const CREATE_INCIDENT_RESPONSE_KEY_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyHelpText', + { + defaultMessage: 'JSON key in create case response that contains the external case id', + } +); + +export const ADD_CASES_VARIABLE = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.addVariable', + { + defaultMessage: 'Add variable', + } +); + +export const GET_INCIDENT_URL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.getIncidentUrlTextFieldLabel', + { + defaultMessage: 'Get Case URL', + } +); +export const GET_INCIDENT_URL_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.getIncidentUrlHelp', + { + defaultMessage: + 'API URL to GET case details JSON from external system. Use the variable selector to add external system id to the url.', + } +); + +export const GET_INCIDENT_TITLE_KEY = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyTextFieldLabel', + { + defaultMessage: 'Get Case Response External Title Key', + } +); +export const GET_INCIDENT_TITLE_KEY_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyHelp', + { + defaultMessage: 'JSON key in get case response that contains the external case title', + } +); + +export const EXTERNAL_INCIDENT_VIEW_URL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.viewIncidentUrlTextFieldLabel', + { + defaultMessage: 'External Case View URL', + } +); +export const EXTERNAL_INCIDENT_VIEW_URL_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.viewIncidentUrlHelp', + { + defaultMessage: + 'URL to view case in external system. Use the variable selector to add external system id or external system title to the url.', + } +); + +export const UPDATE_INCIDENT_METHOD = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.updateIncidentMethodTextFieldLabel', + { + defaultMessage: 'Update Case Method', + } +); + +export const UPDATE_INCIDENT_URL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.updateIncidentUrlTextFieldLabel', + { + defaultMessage: 'Update Case URL', + } +); +export const UPDATE_INCIDENT_URL_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.updateIncidentUrlHelp', + { + defaultMessage: 'API URL to update case.', + } +); + +export const UPDATE_INCIDENT_JSON = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.updateIncidentJsonTextFieldLabel', + { + defaultMessage: 'Update Case Object', + } +); +export const UPDATE_INCIDENT_JSON_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.updateIncidentJsonHelpl', + { + defaultMessage: + 'JSON object to update case. Use the variable selector to add Cases data to the payload.', + } +); + +export const CREATE_COMMENT_METHOD = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createCommentMethodTextFieldLabel', + { + defaultMessage: 'Create Comment Method', + } +); +export const CREATE_COMMENT_URL = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createCommentUrlTextFieldLabel', + { + defaultMessage: 'Create Comment URL', + } +); + +export const CREATE_COMMENT_URL_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createCommentUrlHelp', + { + defaultMessage: 'API URL to add comment to case.', + } +); + +export const CREATE_COMMENT_JSON = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createCommentJsonTextFieldLabel', + { + defaultMessage: 'Create Comment Object', + } +); +export const CREATE_COMMENT_JSON_HELP = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.createCommentJsonHelp', + { + defaultMessage: + 'JSON object to create a comment. Use the variable selector to add Cases data to the payload.', + } +); + +export const HAS_AUTH = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.hasAuthSwitchLabel', + { + defaultMessage: 'Require authentication for this webhook', + } +); + +export const USERNAME = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.userTextFieldLabel', + { + defaultMessage: 'Username', + } +); + +export const PASSWORD = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.passwordTextFieldLabel', + { + defaultMessage: 'Password', + } +); + +export const HEADERS_SWITCH = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.viewHeadersSwitch', + { + defaultMessage: 'Add HTTP header', + } +); + +export const HEADERS_TITLE = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.httpHeadersTitle', + { + defaultMessage: 'Headers in use', + } +); + +export const AUTH_TITLE = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.authenticationLabel', + { + defaultMessage: 'Authentication', + } +); + +export const STEP_1 = i18n.translate('xpack.stackConnectors.components.casesWebhook.step1', { + defaultMessage: 'Set up connector', +}); + +export const STEP_2 = i18n.translate('xpack.stackConnectors.components.casesWebhook.step2', { + defaultMessage: 'Create case', +}); + +export const STEP_2_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.step2Description', + { + defaultMessage: + 'Set fields to create the case in the external system. Check your service’s API documentation to understand what fields are required', + } +); + +export const STEP_3 = i18n.translate('xpack.stackConnectors.components.casesWebhook.step3', { + defaultMessage: 'Get case information', +}); + +export const STEP_3_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.step3Description', + { + defaultMessage: + 'Set fields to add comments to the case in external system. For some systems, this may be the same method as creating updates in cases. Check your service’s API documentation to understand what fields are required.', + } +); + +export const STEP_4 = i18n.translate('xpack.stackConnectors.components.casesWebhook.step4', { + defaultMessage: 'Comments and updates', +}); + +export const STEP_4A = i18n.translate('xpack.stackConnectors.components.casesWebhook.step4a', { + defaultMessage: 'Create update in case', +}); + +export const STEP_4A_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.step4aDescription', + { + defaultMessage: + 'Set fields to create updates to the case in external system. For some systems, this may be the same method as adding comments to cases.', + } +); + +export const STEP_4B = i18n.translate('xpack.stackConnectors.components.casesWebhook.step4b', { + defaultMessage: 'Add comment in case', +}); + +export const STEP_4B_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.step4bDescription', + { + defaultMessage: + 'Set fields to add comments to the case in external system. For some systems, this may be the same method as creating updates in cases.', + } +); + +export const NEXT = i18n.translate('xpack.stackConnectors.components.casesWebhook.next', { + defaultMessage: 'Next', +}); + +export const PREVIOUS = i18n.translate('xpack.stackConnectors.components.casesWebhook.previous', { + defaultMessage: 'Previous', +}); + +export const CASE_TITLE_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.caseTitleDesc', + { + defaultMessage: 'Kibana case title', + } +); + +export const CASE_DESCRIPTION_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.caseDescriptionDesc', + { + defaultMessage: 'Kibana case description', + } +); + +export const CASE_TAGS_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.caseTagsDesc', + { + defaultMessage: 'Kibana case tags', + } +); + +export const CASE_COMMENT_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.caseCommentDesc', + { + defaultMessage: 'Kibana case comment', + } +); + +export const EXTERNAL_ID_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.externalIdDesc', + { + defaultMessage: 'External system id', + } +); + +export const EXTERNAL_TITLE_DESC = i18n.translate( + 'xpack.stackConnectors.components.casesWebhook.externalTitleDesc', + { + defaultMessage: 'External system title', + } +); + +export const DOC_LINK = i18n.translate('xpack.stackConnectors.components.casesWebhook.docLink', { + defaultMessage: 'Configuring Webhook - Case Management connector.', +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/types.ts similarity index 82% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/types.ts index 36e072cc8db4..68f560dd5941 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/types.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { CasesWebhookPublicConfigurationType, CasesWebhookSecretConfigurationType, ExecutorSubActionPushParams, -} from '@kbn/stack-connectors-plugin/server/connector_types/cases/cases_webhook/types'; -import { UserConfiguredActionConnector } from '../../../../types'; +} from '../../../../server/connector_types/cases/cases_webhook/types'; export interface CasesWebhookActionParams { subAction: string; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/validator.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/validator.ts similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/validator.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/validator.ts index 7f34f76807e5..d3d7f6dc8e61 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/validator.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/validator.ts @@ -11,9 +11,9 @@ import { ValidationFunc, } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { containsChars, isUrl } from '@kbn/es-ui-shared-plugin/static/validators/string'; +import { templateActionVariable } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; import { casesVars, commentVars, urlVars, urlVarsExt } from './action_variables'; -import { templateActionVariable } from '../../../lib'; const errorCode: ERROR_CODE = 'ERR_FIELD_MISSING'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.test.tsx new file mode 100644 index 000000000000..14e767915515 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const CONNECTOR_TYPE_ID = '.cases-webhook'; +let connectorTypeModel: ConnectorTypeModel; + +beforeAll(() => { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('logoWebhook'); + }); +}); + +describe('webhook action params validation', () => { + test('action params validation succeeds when action params is valid', async () => { + const actionParams = { + subActionParams: { incident: { title: 'some title {{test}}' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { 'subActionParams.incident.title': [] }, + }); + }); + + test('params validation fails when body is not valid', async () => { + const actionParams = { + subActionParams: { incident: { title: '' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + 'subActionParams.incident.title': ['Title is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.tsx similarity index 80% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.tsx index 5ac8d915e26d..6e86df15c09c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook.tsx @@ -7,10 +7,13 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; import { CasesWebhookActionParams, CasesWebhookConfig, CasesWebhookSecrets } from './types'; -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< CasesWebhookConfig, CasesWebhookSecrets, CasesWebhookActionParams @@ -19,14 +22,14 @@ export function getActionType(): ActionTypeModel< id: '.cases-webhook', iconClass: 'logoWebhook', selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.selectMessageText', + 'xpack.stackConnectors.components.casesWebhook.selectMessageText', { defaultMessage: 'Send a request to a Case Management web service.', } ), isExperimental: true, actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.actionTypeTitle', + 'xpack.stackConnectors.components.casesWebhookxpack.stackConnectors.components.casesWebhook.connectorTypeTitle', { defaultMessage: 'Webhook - Case Management data', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.test.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.test.tsx index ba0cf756530e..fbe24f5e5d86 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.test.tsx @@ -7,15 +7,15 @@ import React from 'react'; import CasesWebhookActionConnectorFields from './webhook_connectors'; -import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../test_utils'; +import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../../lib/test_utils'; import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { MockCodeEditor } from '../../../code_editor.mock'; +import { MockCodeEditor } from '@kbn/triggers-actions-ui-plugin/public/application/code_editor.mock'; import * as i18n from './translations'; -const kibanaReactPath = '../../../../../../../../src/plugins/kibana_react/public'; +const kibanaReactPath = '../../../../../../../src/plugins/kibana_react/public'; -jest.mock('../../../../common/lib/kibana', () => { - const originalModule = jest.requireActual('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public', () => { + const originalModule = jest.requireActual('@kbn/triggers-actions-ui-plugin/public'); return { ...originalModule, useKibana: () => ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.tsx index 29983935cf7f..73e424901469 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_connectors.tsx @@ -17,8 +17,8 @@ import { EuiStepsHorizontal, EuiStepStatus, } from '@elastic/eui'; -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionConnectorFieldsProps } from '../../../../types'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; import { AuthStep, CreateStep, GetStep, UpdateStep } from './steps'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.test.tsx similarity index 93% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.test.tsx index 91adb9616c4a..f931fd1eedda 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.test.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import WebhookParamsFields from './webhook_params'; -import { MockCodeEditor } from '../../../code_editor.mock'; +import { MockCodeEditor } from '@kbn/triggers-actions-ui-plugin/public/application/code_editor.mock'; import { CasesWebhookActionConnector } from './types'; -const kibanaReactPath = '../../../../../../../../src/plugins/kibana_react/public'; +const kibanaReactPath = '../../../../../../../src/plugins/kibana_react/public'; jest.mock(kibanaReactPath, () => { const original = jest.requireActual(kibanaReactPath); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.tsx similarity index 84% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.tsx index 2d1f8b03bd08..dab3f8cc9583 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/cases_webhook/webhook_params.tsx @@ -8,20 +8,22 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut, EuiComboBox, EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { ActionParamsProps } from '../../../../types'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; import { CasesWebhookActionConnector, CasesWebhookActionParams } from './types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; const CREATE_COMMENT_WARNING_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningTitle', + 'xpack.stackConnectors.components.casesWebhook.createCommentWarningTitle', { defaultMessage: 'Unable to share case comments', } ); const CREATE_COMMENT_WARNING_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningDesc', + 'xpack.stackConnectors.components.casesWebhook.createCommentWarningDesc', { defaultMessage: 'Configure the Create Comment URL and Create Comment Objects fields for the connector to share comments externally.', @@ -108,12 +110,9 @@ const WebhookParamsFields: React.FunctionComponent 0 && incident.title !== undefined } - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.titleFieldLabel', - { - defaultMessage: 'Summary (required)', - } - )} + label={i18n.translate('xpack.stackConnectors.components.casesWebhook.titleFieldLabel', { + defaultMessage: 'Summary (required)', + })} > 0 ? comments[0].comment : undefined} label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.commentsTextAreaFieldLabel', + 'xpack.stackConnectors.components.casesWebhook.commentsTextAreaFieldLabel', { defaultMessage: 'Additional comments', } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts new file mode 100644 index 000000000000..22f32cf63603 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getCasesWebhookConnectorType } from './cases_webhook'; +export { getJiraConnectorType } from './jira'; +export { getResilientConnectorType } from './resilient'; +export { + getServiceNowITSMConnectorType, + getServiceNowSIRConnectorType, + getServiceNowITOMConnectorType, +} from './servicenow'; +export { getSwimlaneConnectorType } from './swimlane'; +export { getXmattersConnectorType } from './xmatters'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/api.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/api.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/api.ts similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/api.ts index f213bbc7bda4..e6db358b7988 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/api.ts @@ -6,9 +6,11 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; -import { BASE_ACTION_API_PATH } from '../../../constants'; -import { ConnectorExecutorResult, rewriteResponseToCamelCase } from '../rewrite_response_body'; +import { ActionTypeExecutorResult, BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; +import { + ConnectorExecutorResult, + rewriteResponseToCamelCase, +} from '../../lib/rewrite_response_body'; import { Fields, Issue, IssueTypes } from './types'; export async function getIssueTypes({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/index.ts similarity index 80% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/index.ts index a597a5ea6df4..184109b17cc9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getActionType as getWebhookActionType } from './webhook'; +export { getConnectorType as getJiraConnectorType } from './jira'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.test.tsx similarity index 57% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.test.tsx index 07aa45cfe192..2f8299ea010d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.test.tsx @@ -5,26 +5,26 @@ * 2.0. */ -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; -const ACTION_TYPE_ID = '.jira'; -let actionTypeModel: ActionTypeModel; +const CONNECTOR_TYPE_ID = '.jira'; +let connectorTypeModel: ConnectorTypeModel; beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); if (getResult !== null) { - actionTypeModel = getResult; + connectorTypeModel = getResult; } }); -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); }); }); @@ -34,7 +34,7 @@ describe('jira action params validation', () => { subActionParams: { incident: { summary: 'some title {{test}}' }, comments: [] }, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.summary': [], 'subActionParams.incident.labels': [] }, }); }); @@ -44,7 +44,7 @@ describe('jira action params validation', () => { subActionParams: { incident: { summary: '' }, comments: [] }, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.summary': ['Summary is required.'], 'subActionParams.incident.labels': [], @@ -60,7 +60,7 @@ describe('jira action params validation', () => { }, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.summary': [], 'subActionParams.incident.labels': ['Labels cannot contain spaces.'], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.tsx similarity index 80% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.tsx index 627429a39b5b..92a98a3270cc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira.tsx @@ -7,24 +7,24 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { GenericValidationResult, ActionTypeModel } from '../../../../types'; +import type { + GenericValidationResult, + ActionTypeModel as ConnectorTypeModel, +} from '@kbn/triggers-actions-ui-plugin/public'; import { JiraConfig, JiraSecrets, JiraActionParams } from './types'; -export const JIRA_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.selectMessageText', - { - defaultMessage: 'Create an incident in Jira.', - } -); +export const JIRA_DESC = i18n.translate('xpack.stackConnectors.components.jira.selectMessageText', { + defaultMessage: 'Create an incident in Jira.', +}); export const JIRA_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle', + 'xpack.stackConnectors.components.jira.connectorTypeTitle', { defaultMessage: 'Jira', } ); -export function getActionType(): ActionTypeModel { +export function getConnectorType(): ConnectorTypeModel { return { id: '.jira', iconClass: lazy(() => import('./logo')), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.test.tsx index 3afb0d16f8cb..17166ae5c05c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import JiraConnectorFields from './jira_connectors'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('JiraActionConnectorFields renders', () => { test('Jira connector fields are rendered', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.tsx similarity index 86% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.tsx index 5d056281cd38..781b60569659 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_connectors.tsx @@ -6,15 +6,13 @@ */ import React from 'react'; - -import { ActionConnectorFieldsProps } from '../../../../types'; - -import * as i18n from './translations'; -import { +import type { + ActionConnectorFieldsProps, ConfigFieldSchema, - SimpleConnectorForm, SecretsFieldSchema, -} from '../../simple_connector_form'; +} from '@kbn/triggers-actions-ui-plugin/public'; +import { SimpleConnectorForm } from '@kbn/triggers-actions-ui-plugin/public'; +import * as i18n from './translations'; const configFormSchema: ConfigFieldSchema[] = [ { id: 'apiUrl', label: i18n.API_URL_LABEL, isUrlField: true }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.test.tsx index 8a478c84b509..3865da8ead3a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.test.tsx @@ -11,11 +11,11 @@ import { useGetIssueTypes } from './use_get_issue_types'; import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; import { useGetIssues } from './use_get_issues'; import { useGetSingleIssue } from './use_get_single_issue'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { act, fireEvent, render, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); jest.mock('./use_get_issue_types'); jest.mock('./use_get_fields_by_issue_type'); jest.mock('./use_get_issues'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.tsx similarity index 90% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.tsx index 5228fbcaf119..f4aa607f7cf3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/jira_params.tsx @@ -18,15 +18,16 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; - -import { ActionParamsProps } from '../../../../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import { JiraActionParams } from './types'; import { useGetIssueTypes } from './use_get_issue_types'; import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; import { SearchIssues } from './search_issues'; -import { useKibana } from '../../../../common/lib/kibana'; const JiraParamsFields: React.FunctionComponent> = ({ actionConnector, @@ -198,12 +199,9 @@ const JiraParamsFields: React.FunctionComponent 0 && incident.summary !== undefined } - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.summaryFieldLabel', - { - defaultMessage: 'Summary (required)', - } - )} + label={i18n.translate('xpack.stackConnectors.components.jira.summaryFieldLabel', { + defaultMessage: 'Summary (required)', + })} > 0 ? comments[0].comment : undefined} label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel', + 'xpack.stackConnectors.components.jira.commentsTextAreaFieldLabel', { defaultMessage: 'Additional comments', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/logo.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/logo.tsx index f42b57140850..945dc955e4b2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => ( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueMessage', - { - defaultMessage: 'Unable to get issue with id {id}', - values: { id }, - } - ); + i18n.translate('xpack.stackConnectors.components.jira.unableToGetIssueMessage', { + defaultMessage: 'Unable to get issue with id {id}', + values: { id }, + }); export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel', + 'xpack.stackConnectors.components.jira.searchIssuesComboBoxAriaLabel', { defaultMessage: 'Type to search', } ); export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder', + 'xpack.stackConnectors.components.jira.searchIssuesComboBoxPlaceholder', { defaultMessage: 'Type to search', } ); export const SEARCH_ISSUES_LOADING = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading', + 'xpack.stackConnectors.components.jira.searchIssuesLoading', { defaultMessage: 'Loading...', } ); export const LABELS_WHITE_SPACES = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage', + 'xpack.stackConnectors.components.jira.labelsSpacesErrorMessage', { defaultMessage: 'Labels cannot contain spaces.', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/types.ts similarity index 81% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/types.ts index 85e7be1626b0..e5796f44591c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/types.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { ExecutorSubActionPushParams } from '@kbn/stack-connectors-plugin/server/connector_types/cases/jira/types'; -import { UserConfiguredActionConnector } from '../../../../types'; +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { ExecutorSubActionPushParams } from '../../../../server/connector_types/cases/jira/types'; export type JiraActionConnector = UserConfiguredActionConnector; export interface JiraActionParams { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_fields_by_issue_type.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_fields_by_issue_type.tsx index 4f0e1e143291..11b8c7fee829 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_fields_by_issue_type.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { Fields } from './types'; import { getFieldsByIssueType } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issue_types.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issue_types.tsx index 97db74630258..ed5a0e0a4819 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issue_types.tsx @@ -8,7 +8,7 @@ import { useState, useEffect, useRef } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { IssueTypes } from './types'; import { getIssueTypes } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issues.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issues.tsx index 0dea608b78ac..04153fdd5e4f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_issues.tsx @@ -8,7 +8,7 @@ import { isEmpty, debounce } from 'lodash/fp'; import { useState, useEffect, useRef } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { Issue } from './types'; import { getIssues } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_single_issue.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_single_issue.tsx index 3967ab25c889..bacc57c971ad 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/jira/use_get_single_issue.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { Issue } from './types'; import { getIssue } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/api.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/api.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/api.ts similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/api.ts index b0378be04efd..80341d45402e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/api.ts @@ -6,8 +6,11 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { BASE_ACTION_API_PATH } from '../../../constants'; -import { ConnectorExecutorResult, rewriteResponseToCamelCase } from '../rewrite_response_body'; +import { BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; +import { + ConnectorExecutorResult, + rewriteResponseToCamelCase, +} from '../../lib/rewrite_response_body'; export async function getIncidentTypes({ http, diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/index.ts new file mode 100644 index 000000000000..0d6ca3e87e73 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getResilientConnectorType } from './resilient'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/logo.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/logo.tsx index 7b64a1330d40..7437984034bd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => ( { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + }); +}); + +describe('resilient action params validation', () => { + test('action params validation succeeds when action params is valid', async () => { + const actionParams = { + subActionParams: { incident: { name: 'some title {{test}}' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { 'subActionParams.incident.name': [] }, + }); + }); + + test('params validation fails when body is not valid', async () => { + const actionParams = { + subActionParams: { incident: { name: '' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + 'subActionParams.incident.name': ['Name is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient.tsx similarity index 77% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient.tsx index 2297107e914c..9ec583762b97 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient.tsx @@ -7,24 +7,24 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { GenericValidationResult, ActionTypeModel } from '../../../../types'; +import type { + GenericValidationResult, + ActionTypeModel as ConnectorTypeModel, +} from '@kbn/triggers-actions-ui-plugin/public'; import { ResilientConfig, ResilientSecrets, ResilientActionParams } from './types'; -export const DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText', - { - defaultMessage: 'Create an incident in IBM Resilient.', - } -); +export const DESC = i18n.translate('xpack.stackConnectors.components.resilient.selectMessageText', { + defaultMessage: 'Create an incident in IBM Resilient.', +}); export const TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle', + 'xpack.stackConnectors.components.resilient.connectorTypeTitle', { defaultMessage: 'Resilient', } ); -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< ResilientConfig, ResilientSecrets, ResilientActionParams diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.test.tsx index 0fbe63b28b16..8364e614a033 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import ResilientConnectorFields from './resilient_connectors'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('ResilientActionConnectorFields renders', () => { test('alerting Resilient connector fields are rendered', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.tsx similarity index 86% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.tsx index 6cc4d31f1b40..ddd219d4fc52 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_connectors.tsx @@ -6,14 +6,13 @@ */ import React from 'react'; - -import { ActionConnectorFieldsProps } from '../../../../types'; -import * as i18n from './translations'; -import { +import type { + ActionConnectorFieldsProps, ConfigFieldSchema, SecretsFieldSchema, - SimpleConnectorForm, -} from '../../simple_connector_form'; +} from '@kbn/triggers-actions-ui-plugin/public'; +import { SimpleConnectorForm } from '@kbn/triggers-actions-ui-plugin/public'; +import * as i18n from './translations'; const configFormSchema: ConfigFieldSchema[] = [ { id: 'apiUrl', label: i18n.API_URL_LABEL, isUrlField: true }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.test.tsx index 09ae6fe002d4..c6417660720f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.test.tsx @@ -14,7 +14,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; jest.mock('./use_get_incident_types'); jest.mock('./use_get_severity'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; const useGetSeverityMock = useGetSeverity as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.tsx similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.tsx index a50736b8c737..04a0ba4f85ec 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/resilient_params.tsx @@ -16,15 +16,16 @@ import { EuiSelectOption, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - -import { ActionParamsProps } from '../../../../types'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import { ResilientActionParams } from './types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; import { useGetIncidentTypes } from './use_get_incident_types'; import { useGetSeverity } from './use_get_severity'; -import { useKibana } from '../../../../common/lib/kibana'; const ResilientParamsFields: React.FunctionComponent> = ({ actionConnector, @@ -169,7 +170,7 @@ const ResilientParamsFields: React.FunctionComponent @@ -188,10 +189,9 @@ const ResilientParamsFields: React.FunctionComponent 0 && incident.name !== undefined } - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel', - { defaultMessage: 'Name (required)' } - )} + label={i18n.translate('xpack.stackConnectors.components.resilient.nameFieldLabel', { + defaultMessage: 'Name (required)', + })} > @@ -245,7 +244,7 @@ const ResilientParamsFields: React.FunctionComponent 0 ? comments[0].comment : undefined} label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel', + 'xpack.stackConnectors.components.resilient.commentsTextAreaFieldLabel', { defaultMessage: 'Additional comments' } )} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/translations.ts similarity index 55% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/translations.ts index df4c15b0f322..d049ba633e69 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/translations.ts @@ -8,49 +8,46 @@ import { i18n } from '@kbn/i18n'; export const API_URL_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel', + 'xpack.stackConnectors.components.resilient.apiUrlTextFieldLabel', { defaultMessage: 'URL', } ); -export const ORG_ID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId', - { - defaultMessage: 'Organization ID', - } -); +export const ORG_ID_LABEL = i18n.translate('xpack.stackConnectors.components.resilient.orgId', { + defaultMessage: 'Organization ID', +}); export const API_KEY_ID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId', + 'xpack.stackConnectors.components.resilient.apiKeyId', { defaultMessage: 'API Key ID', } ); export const API_KEY_SECRET_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret', + 'xpack.stackConnectors.components.resilient.apiKeySecret', { defaultMessage: 'API Key Secret', } ); export const NAME_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField', + 'xpack.stackConnectors.components.resilient.requiredNameTextField', { defaultMessage: 'Name is required.', } ); export const INCIDENT_TYPES_API_ERROR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage', + 'xpack.stackConnectors.components.resilient.unableToGetIncidentTypesMessage', { defaultMessage: 'Unable to get incident types', } ); export const SEVERITY_API_ERROR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetSeverityMessage', + 'xpack.stackConnectors.components.resilient.unableToGetSeverityMessage', { defaultMessage: 'Unable to get severity', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/types.ts similarity index 75% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/types.ts index 12c46d290021..67006b967b5d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/types.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { ExecutorSubActionPushParams } from '@kbn/stack-connectors-plugin/server/connector_types/cases/resilient/types'; -import { UserConfiguredActionConnector } from '../../../../types'; +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { ExecutorSubActionPushParams } from '../../../../server/connector_types/cases/resilient/types'; export type ResilientActionConnector = UserConfiguredActionConnector< ResilientConfig, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_incident_types.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_incident_types.tsx index e398f1a8dd32..577af149d77c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_incident_types.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef } from 'react'; import { HttpSetup, ToastsApi } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { getIncidentTypes } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_severity.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_severity.tsx index 79e3b27d0a08..7f93e908dbb9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/resilient/use_get_severity.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef } from 'react'; import { HttpSetup, ToastsApi } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { getSeverity } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts index 95fac75cb9f5..4cf46d57eb7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts @@ -10,12 +10,15 @@ import { HttpSetup } from '@kbn/core/public'; import { ActionTypeExecutorResult, INTERNAL_BASE_ACTION_API_PATH, + BASE_ACTION_API_PATH, } from '@kbn/actions-plugin/common'; -import { snExternalServiceConfig } from '@kbn/stack-connectors-plugin/common/servicenow_config'; -import { BASE_ACTION_API_PATH } from '../../../constants'; +import { snExternalServiceConfig } from '../../../../common/servicenow_config'; import { API_INFO_ERROR } from './translations'; import { AppInfo, RESTApiError, ServiceNowActionConnector } from './types'; -import { ConnectorExecutorResult, rewriteResponseToCamelCase } from '../rewrite_response_body'; +import { + ConnectorExecutorResult, + rewriteResponseToCamelCase, +} from '../../lib/rewrite_response_body'; import { Choice } from './types'; export async function getChoices({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.tsx similarity index 81% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.tsx index 9aefd7a2259b..2938685f1102 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.tsx @@ -11,14 +11,14 @@ import { i18n } from '@kbn/i18n'; import { SNStoreButton } from './sn_store_button'; const content = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.content', + 'xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.content', { defaultMessage: 'Please go to the ServiceNow app store and install the application', } ); const ERROR_MESSAGE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.errorMessage', + 'xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.errorMessage', { defaultMessage: 'Error message', } @@ -39,7 +39,7 @@ const ApplicationRequiredCalloutComponent: React.FC = ({ appId, message } data-test-subj="snApplicationCallout" color="danger" title={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout', + 'xpack.stackConnectors.components.serviceNow.applicationRequiredCallout', { defaultMessage: 'Elastic ServiceNow App not installed', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/credentials_auth.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/credentials_auth.tsx index b996d8feac8e..46b509e22d6f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/credentials_auth.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { PasswordField } from '../../../password_field'; +import { PasswordField } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from '../translations'; interface Props { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/index.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/index.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/oauth.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/oauth.tsx index 4c51641ea0bf..febcb6c49830 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/oauth.tsx @@ -9,8 +9,8 @@ import React, { memo } from 'react'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { TextAreaField, TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { PasswordField } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from '../translations'; -import { PasswordField } from '../../../password_field'; interface Props { readOnly: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx index d0b0b50b0ffc..aab2ab0fb21c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { Credentials } from './credentials'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('Credentials', () => { const connector = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials_api_url.tsx similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials_api_url.tsx index 392b17308034..58130ee8d8a0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials_api_url.tsx @@ -12,7 +12,7 @@ import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; interface Props { @@ -31,7 +31,7 @@ const CredentialsApiUrlComponent: React.FC = ({ isLoading, readOnly, path

= ({ onMigrate }) => { data-test-subj="snDeprecatedCallout" color="warning" title={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutTitle', + 'xpack.stackConnectors.components.serviceNow.deprecatedCalloutTitle', { defaultMessage: 'This connector type is deprecated', } @@ -40,13 +40,13 @@ const DeprecatedCalloutComponent: React.FC = ({ onMigrate }) => { > {i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutCreate', + 'xpack.stackConnectors.components.serviceNow.deprecatedCalloutCreate', { defaultMessage: 'or create a new one.', } @@ -64,7 +64,7 @@ const DeprecatedCalloutComponent: React.FC = ({ onMigrate }) => { export const DeprecatedCallout = memo(DeprecatedCalloutComponent); const updateThisConnectorMessage = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', + 'xpack.stackConnectors.components.serviceNow.deprecatedCalloutMigrate', { defaultMessage: 'Update this connector,', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.test.ts similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.test.ts index a02fca8f2dca..7700014e5802 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.test.ts @@ -11,7 +11,7 @@ import { getConnectorDescriptiveTitle, getSelectedConnectorIcon, } from './helpers'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; const deprecatedConnector: ActionConnector = { secrets: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts index 3798a312083e..def683edbdd3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts @@ -7,9 +7,12 @@ import { lazy, ComponentType } from 'react'; import { EuiSelectOption } from '@elastic/eui'; +import { + ActionConnector, + deprecatedMessage, + IErrorObject, +} from '@kbn/triggers-actions-ui-plugin/public'; import { AppInfo, Choice, RESTApiError } from './types'; -import { ActionConnector, IErrorObject } from '../../../../types'; -import { deprecatedMessage } from '../../../../common/connectors_selection'; export const DEFAULT_CORRELATION_ID = '{{rule.id}}:{{alert.id}}'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts similarity index 73% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts index c313fd5d0edd..553cf2edde84 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts @@ -6,7 +6,7 @@ */ export { - getServiceNowITSMActionType, - getServiceNowSIRActionType, - getServiceNowITOMActionType, + getServiceNowITSMConnectorType, + getServiceNowSIRConnectorType, + getServiceNowITOMConnectorType, } from './servicenow'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/installation_callout.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/installation_callout.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/installation_callout.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/installation_callout.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/logo.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/logo.tsx index e2c5546e31a7..f97b07247569 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; function Logo(props: LogoProps) { return ( diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx new file mode 100644 index 000000000000..9427623f0de8 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const SERVICENOW_ITSM_CONNECTOR_TYPE_ID = '.servicenow'; +const SERVICENOW_SIR_CONNECTOR_TYPE_ID = '.servicenow-sir'; +const SERVICENOW_ITOM_CONNECTOR_TYPE_ID = '.servicenow-itom'; +let connectorTypeRegistry: TypeRegistry; + +beforeAll(() => { + connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); +}); + +describe('connectorTypeRegistry.get() works', () => { + [ + SERVICENOW_ITSM_CONNECTOR_TYPE_ID, + SERVICENOW_SIR_CONNECTOR_TYPE_ID, + SERVICENOW_ITOM_CONNECTOR_TYPE_ID, + ].forEach((id) => { + test(`${id}: connector type static data is as expected`, () => { + const connectorTypeModel = connectorTypeRegistry.get(id); + expect(connectorTypeModel.id).toEqual(id); + }); + }); +}); + +describe('servicenow action params validation', () => { + [SERVICENOW_ITSM_CONNECTOR_TYPE_ID, SERVICENOW_SIR_CONNECTOR_TYPE_ID].forEach((id) => { + test(`${id}: action params validation succeeds when action params is valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(id); + const actionParams = { + subActionParams: { incident: { short_description: 'some title {{test}}' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['subActionParams.incident.short_description']: [] }, + }); + }); + + test(`${id}: params validation fails when short_description is not valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(id); + const actionParams = { + subActionParams: { incident: { short_description: '' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + ['subActionParams.incident.short_description']: ['Short description is required.'], + }, + }); + }); + }); + + test(`${SERVICENOW_ITOM_CONNECTOR_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITOM_CONNECTOR_TYPE_ID); + const actionParams = { subActionParams: { severity: 'Critical' } }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['severity']: [] }, + }); + }); + + test(`${SERVICENOW_ITOM_CONNECTOR_TYPE_ID}: params validation fails when severity is not valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITOM_CONNECTOR_TYPE_ID); + const actionParams = { subActionParams: { severity: null } }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['severity']: ['Severity is required.'] }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx similarity index 84% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx index 85b2aea86284..932d244e852f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx @@ -7,7 +7,10 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; import { ServiceNowConfig, ServiceNowITOMActionParams, @@ -18,48 +21,48 @@ import { import { getConnectorDescriptiveTitle, getSelectedConnectorIcon } from './helpers'; export const SERVICENOW_ITOM_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.actionTypeTitle', + 'xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle', { defaultMessage: 'ServiceNow ITOM', } ); export const SERVICENOW_ITOM_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.selectMessageText', + 'xpack.stackConnectors.components.serviceNowITOM.selectMessageText', { defaultMessage: 'Create an event in ServiceNow ITOM.', } ); export const SERVICENOW_ITSM_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.selectMessageText', + 'xpack.stackConnectors.components.serviceNowITSM.selectMessageText', { defaultMessage: 'Create an incident in ServiceNow ITSM.', } ); export const SERVICENOW_SIR_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.selectMessageText', + 'xpack.stackConnectors.components.serviceNowSIR.selectMessageText', { defaultMessage: 'Create an incident in ServiceNow SecOps.', } ); export const SERVICENOW_ITSM_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.actionTypeTitle', + 'xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle', { defaultMessage: 'ServiceNow ITSM', } ); export const SERVICENOW_SIR_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.actionTypeTitle', + 'xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle', { defaultMessage: 'ServiceNow SecOps', } ); -export function getServiceNowITSMActionType(): ActionTypeModel< +export function getServiceNowITSMConnectorType(): ConnectorTypeModel< ServiceNowConfig, ServiceNowSecrets, ServiceNowITSMActionParams @@ -97,7 +100,7 @@ export function getServiceNowITSMActionType(): ActionTypeModel< }; } -export function getServiceNowSIRActionType(): ActionTypeModel< +export function getServiceNowSIRConnectorType(): ConnectorTypeModel< ServiceNowConfig, ServiceNowSecrets, ServiceNowSIRActionParams @@ -135,7 +138,7 @@ export function getServiceNowSIRActionType(): ActionTypeModel< }; } -export function getServiceNowITOMActionType(): ActionTypeModel< +export function getServiceNowITOMConnectorType(): ConnectorTypeModel< ServiceNowConfig, ServiceNowSecrets, ServiceNowITOMActionParams diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx index f3ec5594144e..6bf81f5aeae7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx @@ -10,17 +10,17 @@ import { act, within } from '@testing-library/react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { render, act as reactAct } from '@testing-library/react'; -import { ConnectorValidationFunc } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; -import { updateActionConnector } from '../../../lib/action_connector_api'; +import { ConnectorValidationFunc } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; +import { updateActionConnector } from '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api'; import ServiceNowConnectorFields from './servicenow_connectors'; import { getAppInfo } from './api'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import { mount } from 'enzyme'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); -jest.mock('../../../lib/action_connector_api'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api'); jest.mock('./api'); const useKibanaMock = useKibana as jest.Mocked; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.tsx similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.tsx index d9e462ae552d..b9aeca98b6cd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.tsx @@ -8,23 +8,24 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { snExternalServiceConfig } from '@kbn/stack-connectors-plugin/common/servicenow_config'; import { useFormContext, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; - -import { ActionConnectorFieldsProps } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + HiddenField, + updateActionConnector, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; +import type { ConnectorFormSchema } from '@kbn/triggers-actions-ui-plugin/public'; +import { snExternalServiceConfig } from '../../../../common/servicenow_config'; import { DeprecatedCallout } from './deprecated_callout'; import { useGetAppInfo } from './use_get_app_info'; import { ApplicationRequiredCallout } from './application_required_callout'; import { isRESTApiError } from './helpers'; import { InstallationCallout } from './installation_callout'; import { UpdateConnector, UpdateConnectorFormSchema } from './update_connector'; -import { updateActionConnector } from '../../../lib/action_connector_api'; import { Credentials } from './credentials'; import * as i18n from './translations'; import { ServiceNowActionConnector, ServiceNowConfig, ServiceNowSecrets } from './types'; -import { HiddenField } from '../../hidden_field'; -import { ConnectorFormSchema } from '../../../sections/action_connector_form/types'; // eslint-disable-next-line import/no-default-export export { ServiceNowConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx index a0dda6edf76e..e70005f8c7e1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx @@ -8,7 +8,11 @@ import { act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { AppMockRenderer, ConnectorFormTestProvider, createAppMockRenderer } from '../test_utils'; +import { + AppMockRenderer, + ConnectorFormTestProvider, + createAppMockRenderer, +} from '../../lib/test_utils'; import ServiceNowConnectorFieldsNoApp from './servicenow_connectors_no_app'; describe('ServiceNowActionConnectorFields renders', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.tsx similarity index 85% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.tsx index e4332f163151..d1a2f3472acb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.tsx @@ -7,10 +7,9 @@ import React from 'react'; import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; - -import { ActionConnectorFieldsProps } from '../../../../types'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { ConnectorFormSchema } from '@kbn/triggers-actions-ui-plugin/public'; import { Credentials } from './credentials'; -import { ConnectorFormSchema } from '../../../sections/action_connector_form/types'; import { ServiceNowConfig, ServiceNowSecrets } from './types'; const ServiceNowConnectorFieldsNoApp: React.FC = ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx index d17c77da1f82..60531c7a7104 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { mount } from 'enzyme'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { useChoices } from './use_choices'; import ServiceNowITOMParamsFields from './servicenow_itom_params'; jest.mock('./use_choices'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useChoicesMock = useChoices as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx similarity index 94% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx index 8ad32fc0bc86..caa2f40bac2c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx @@ -7,10 +7,12 @@ import React, { useCallback, useEffect, useRef, useMemo } from 'react'; import { EuiFormRow, EuiSpacer, EuiTitle, EuiSelect } from '@elastic/eui'; -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionParamsProps } from '../../../../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; import { useChoices } from './use_choices'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx index f8375a5aaeb6..aa6cb6c71278 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { act } from '@testing-library/react'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { useGetChoices } from './use_get_choices'; import ServiceNowITSMParamsFields from './servicenow_itsm_params'; import { Choice } from './types'; import { merge } from 'lodash'; jest.mock('./use_get_choices'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useGetChoicesMock = useGetChoices as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx index 3bae1c3b858d..a585ee48864e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx @@ -16,12 +16,13 @@ import { EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; - -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionParamsProps } from '../../../../types'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import { ServiceNowITSMActionParams, Choice, Fields } from './types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; import { useGetChoices } from './use_get_choices'; import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; @@ -252,7 +253,7 @@ const ServiceNowParamsFields: React.FunctionComponent< helpText={ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_selection_row.tsx similarity index 81% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_selection_row.tsx index 65068c6f56a0..f1fbb1a009ec 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_selection_row.tsx @@ -8,8 +8,8 @@ import { EuiIconTip } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionConnector } from '../../../../types'; -import { connectorDeprecatedMessage } from '../../../../common/connectors_selection'; +import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; +import { connectorDeprecatedMessage } from '@kbn/triggers-actions-ui-plugin/public'; // eslint-disable-next-line import/no-default-export export { ServiceNowSelectableRowIcon as default }; @@ -32,7 +32,7 @@ export function ServiceNowSelectableRowIcon({ } const deprecatedTooltipTitle = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle', + 'xpack.stackConnectors.components.serviceNow.deprecatedTooltipTitle', { defaultMessage: 'Deprecated connector', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx index 9f15cb07f92e..873993889162 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { act } from '@testing-library/react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { useGetChoices } from './use_get_choices'; import ServiceNowSIRParamsFields from './servicenow_sir_params'; import { Choice } from './types'; import { merge } from 'lodash'; jest.mock('./use_get_choices'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useGetChoicesMock = useGetChoices as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx index a341dca3f255..e58d635f9ef2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx @@ -16,11 +16,12 @@ import { EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; - -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionParamsProps } from '../../../../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; import { useGetChoices } from './use_get_choices'; @@ -239,7 +240,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< helpText={ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/translations.ts similarity index 50% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/translations.ts index 7c6fab2b1879..191fc001d7e8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/translations.ts @@ -8,146 +8,143 @@ import { i18n } from '@kbn/i18n'; export const API_URL_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.apiUrlTextFieldLabel', { defaultMessage: 'ServiceNow instance URL', } ); export const API_URL_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField', + 'xpack.stackConnectors.components.serviceNow.invalidApiUrlTextField', { defaultMessage: 'URL is invalid.', } ); export const AUTHENTICATION_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.authenticationLabel', + 'xpack.stackConnectors.components.serviceNow.authenticationLabel', { defaultMessage: 'Authentication', } ); export const USERNAME_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.usernameTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.usernameTextFieldLabel', { defaultMessage: 'Username', } ); export const USERNAME_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField', + 'xpack.stackConnectors.components.serviceNow.requiredUsernameTextField', { defaultMessage: 'Username is required.', } ); export const PASSWORD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.passwordTextFieldLabel', { defaultMessage: 'Password', } ); export const TITLE_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.common.requiredShortDescTextField', + 'xpack.stackConnectors.components.serviceNow.requiredShortDescTextField', { defaultMessage: 'Short description is required.', } ); -export const INCIDENT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.title', - { - defaultMessage: 'Incident', - } -); +export const INCIDENT = i18n.translate('xpack.stackConnectors.components.serviceNow.title', { + defaultMessage: 'Incident', +}); export const SECURITY_INCIDENT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenowSIR.title', + 'xpack.stackConnectors.components.serviceNowSIR.title', { defaultMessage: 'Security Incident', } ); export const SHORT_DESCRIPTION_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.titleFieldLabel', + 'xpack.stackConnectors.components.serviceNow.titleFieldLabel', { defaultMessage: 'Short description (required)', } ); export const DESCRIPTION_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.descriptionTextAreaFieldLabel', { defaultMessage: 'Description', } ); export const COMMENTS_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.commentsTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.commentsTextAreaFieldLabel', { defaultMessage: 'Additional comments', } ); export const CHOICES_API_ERROR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unableToGetChoicesMessage', + 'xpack.stackConnectors.components.serviceNow.unableToGetChoicesMessage', { defaultMessage: 'Unable to get choices', } ); export const CATEGORY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.categoryTitle', + 'xpack.stackConnectors.components.serviceNow.categoryTitle', { defaultMessage: 'Category', } ); export const SUBCATEGORY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.subcategoryTitle', + 'xpack.stackConnectors.components.serviceNow.subcategoryTitle', { defaultMessage: 'Subcategory', } ); export const URGENCY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.urgencySelectFieldLabel', + 'xpack.stackConnectors.components.serviceNow.urgencySelectFieldLabel', { defaultMessage: 'Urgency', } ); export const SEVERITY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severitySelectFieldLabel', + 'xpack.stackConnectors.components.serviceNow.severitySelectFieldLabel', { defaultMessage: 'Severity', } ); export const IMPACT_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel', + 'xpack.stackConnectors.components.serviceNow.impactSelectFieldLabel', { defaultMessage: 'Impact', } ); export const PRIORITY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel', + 'xpack.stackConnectors.components.serviceNow.prioritySelectFieldLabel', { defaultMessage: 'Priority', } ); export const API_INFO_ERROR = (status: number) => - i18n.translate('xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError', { + i18n.translate('xpack.stackConnectors.components.serviceNow.apiInfoError', { values: { status }, defaultMessage: 'Received status: {status} when attempting to get application information', }); export const FETCH_ERROR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.fetchErrorMsg', + 'xpack.stackConnectors.components.serviceNow.fetchErrorMsg', { defaultMessage: 'Failed to fetch. Check the URL or the CORS configuration of your ServiceNow instance.', @@ -155,7 +152,7 @@ export const FETCH_ERROR = i18n.translate( ); export const INSTALLATION_CALLOUT_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle', + 'xpack.stackConnectors.components.serviceNow.installationCalloutTitle', { defaultMessage: 'To use this connector, first install the Elastic app from the ServiceNow app store.', @@ -163,218 +160,206 @@ export const INSTALLATION_CALLOUT_TITLE = i18n.translate( ); export const UPDATE_SUCCESS_TOAST_TITLE = (connectorName: string) => - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateSuccessToastTitle', - { - defaultMessage: '{connectorName} connector updated', - values: { - connectorName, - }, - } - ); + i18n.translate('xpack.stackConnectors.components.serviceNow.updateSuccessToastTitle', { + defaultMessage: '{connectorName} connector updated', + values: { + connectorName, + }, + }); export const UPDATE_SUCCESS_TOAST_TEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateCalloutText', + 'xpack.stackConnectors.components.serviceNow.updateCalloutText', { defaultMessage: 'Connector has been updated.', } ); export const VISIT_SN_STORE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.visitSNStore', + 'xpack.stackConnectors.components.serviceNow.visitSNStore', { defaultMessage: 'Visit ServiceNow app store', } ); export const SETUP_DEV_INSTANCE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance', + 'xpack.stackConnectors.components.serviceNow.setupDevInstance', { defaultMessage: 'setup a developer instance', } ); export const SN_INSTANCE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.snInstanceLabel', + 'xpack.stackConnectors.components.serviceNow.snInstanceLabel', { defaultMessage: 'ServiceNow instance', } ); -export const UNKNOWN = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unknown', - { - defaultMessage: 'UNKNOWN', - } -); +export const UNKNOWN = i18n.translate('xpack.stackConnectors.components.serviceNow.unknown', { + defaultMessage: 'UNKNOWN', +}); export const CORRELATION_ID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationID', + 'xpack.stackConnectors.components.serviceNow.correlationID', { defaultMessage: 'Correlation ID (optional)', } ); export const CORRELATION_DISPLAY = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationDisplay', + 'xpack.stackConnectors.components.serviceNow.correlationDisplay', { defaultMessage: 'Correlation display (optional)', } ); -export const EVENT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenowITOM.event', - { - defaultMessage: 'Event', - } -); +export const EVENT = i18n.translate('xpack.stackConnectors.components.serviceNowITOM.event', { + defaultMessage: 'Event', +}); /** * ITOM */ export const SOURCE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.sourceTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.sourceTextAreaFieldLabel', { defaultMessage: 'Source', } ); export const EVENT_CLASS = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.eventClassTextAreaFieldLabel', { defaultMessage: 'Source instance', } ); export const RESOURCE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.resourceTextAreaFieldLabel', { defaultMessage: 'Resource', } ); export const NODE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.nodeTextAreaFieldLabel', { defaultMessage: 'Node', } ); export const METRIC_NAME = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.metricNameTextAreaFieldLabel', { defaultMessage: 'Metric name', } ); export const TYPE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.typeTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.typeTextAreaFieldLabel', { defaultMessage: 'Type', } ); export const MESSAGE_KEY = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel', + 'xpack.stackConnectors.components.serviceNow.messageKeyTextAreaFieldLabel', { defaultMessage: 'Message key', } ); export const SEVERITY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField', + 'xpack.stackConnectors.components.serviceNow.requiredSeverityTextField', { defaultMessage: 'Severity is required.', } ); export const SEVERITY_REQUIRED_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel', + 'xpack.stackConnectors.components.serviceNow.severityRequiredSelectFieldLabel', { defaultMessage: 'Severity (required)', } ); export const CLIENTID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientIdTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.clientIdTextFieldLabel', { defaultMessage: 'Client ID', } ); export const CLIENTSECRET_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientSecretTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.clientSecretTextFieldLabel', { defaultMessage: 'Client Secret', } ); export const KEY_ID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.keyIdTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.keyIdTextFieldLabel', { defaultMessage: 'JWT Verifier Key ID', } ); export const USER_IDENTIFIER_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.userEmailTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.userEmailTextFieldLabel', { defaultMessage: 'User Identifier', } ); export const PRIVATE_KEY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.privateKeyTextFieldLabel', { defaultMessage: 'Private Key', } ); export const PRIVATE_KEY_PASSWORD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassTextFieldLabel', + 'xpack.stackConnectors.components.serviceNow.privateKeyPassTextFieldLabel', { defaultMessage: 'Private Key Password', } ); export const PRIVATE_KEY_PASSWORD_HELPER_TEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassLabelHelpText', + 'xpack.stackConnectors.components.serviceNow.privateKeyPassLabelHelpText', { defaultMessage: 'This is only required if you have set a password on your private key', } ); export const CLIENTID_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredClientIdTextField', + 'xpack.stackConnectors.components.serviceNow.requiredClientIdTextField', { defaultMessage: 'Client ID is required.', } ); export const PRIVATE_KEY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPrivateKeyTextField', + 'xpack.stackConnectors.components.serviceNow.requiredPrivateKeyTextField', { defaultMessage: 'Private Key is required.', } ); export const KEYID_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField', + 'xpack.stackConnectors.components.serviceNow.requiredKeyIdTextField', { defaultMessage: 'JWT Verifier Key ID is required.', } ); export const USER_IDENTIFIER_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserIdentifierTextField', + 'xpack.stackConnectors.components.serviceNow.requiredUserIdentifierTextField', { defaultMessage: 'User Identifier is required.', } ); -export const IS_OAUTH = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.useOAuth', - { - defaultMessage: 'Use OAuth authentication', - } -); +export const IS_OAUTH = i18n.translate('xpack.stackConnectors.components.serviceNow.useOAuth', { + defaultMessage: 'Use OAuth authentication', +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts index e6fbcf6e8193..f10de69252f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { ExecutorSubActionPushParamsITSM, ExecutorSubActionPushParamsSIR, ExecutorSubActionAddEventParams, -} from '@kbn/stack-connectors-plugin/server/connector_types/cases/servicenow/types'; -import { UserConfiguredActionConnector } from '../../../../types'; +} from '../../../../server/connector_types/cases/servicenow/types'; export type ServiceNowActionConnector = UserConfiguredActionConnector< ServiceNowConfig, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.test.tsx index 219ba6811485..9a6d7c26e25f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.test.tsx @@ -13,7 +13,7 @@ import { Props, UpdateConnector } from './update_connector'; import { act } from 'react-dom/test-utils'; import { render, act as reactAct } from '@testing-library/react'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const mountUpdateConnector = (props: Partial = {}, isOAuth: boolean = false) => { return mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.tsx similarity index 86% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.tsx index 936e12a564b4..84bfba31ca56 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.tsx @@ -21,58 +21,55 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { snExternalServiceConfig } from '@kbn/stack-connectors-plugin/common/servicenow_config'; import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { snExternalServiceConfig } from '../../../../common/servicenow_config'; import { CredentialsApiUrl } from './credentials_api_url'; import { CredentialsAuth, OAuth } from './auth_types'; import { SNStoreLink } from './sn_store_button'; import { ApplicationRequiredCallout } from './application_required_callout'; import { ServiceNowConfig, ServiceNowSecrets } from './types'; -const title = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle', - { - defaultMessage: 'Update ServiceNow connector', - } -); +const title = i18n.translate('xpack.stackConnectors.components.serviceNow.updateFormTitle', { + defaultMessage: 'Update ServiceNow connector', +}); const step1InstallTitle = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormInstallTitle', + 'xpack.stackConnectors.components.serviceNow.updateFormInstallTitle', { defaultMessage: 'Install the Elastic ServiceNow app', } ); const step2InstanceUrlTitle = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormUrlTitle', + 'xpack.stackConnectors.components.serviceNow.updateFormUrlTitle', { defaultMessage: 'Enter your ServiceNow instance URL', } ); const step3CredentialsTitle = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormCredentialsTitle', + 'xpack.stackConnectors.components.serviceNow.updateFormCredentialsTitle', { defaultMessage: 'Provide authentication credentials', } ); const cancelButtonText = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.cancelButtonText', + 'xpack.stackConnectors.components.serviceNow.cancelButtonText', { defaultMessage: 'Cancel', } ); const confirmButtonText = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.confirmButtonText', + 'xpack.stackConnectors.components.serviceNow.confirmButtonText', { defaultMessage: 'Update', } ); const warningMessage = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.warningMessage', + 'xpack.stackConnectors.components.serviceNow.warningMessage', { defaultMessage: 'This updates all instances of this connector and cannot be reversed.', } @@ -144,7 +141,7 @@ const UpdateConnectorComponent: React.FC = ({ title: step1InstallTitle, children: ( ; const getChoicesMock = getChoices as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.tsx index e3b10896e870..9b1b0e453f4d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.tsx @@ -8,7 +8,7 @@ import { useCallback, useMemo, useState } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { Choice, Fields } from './types'; import { useGetChoices } from './use_get_choices'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.test.tsx index f8e9a2c4bac1..c8c061c9d07f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.test.tsx @@ -13,7 +13,7 @@ import { ServiceNowActionConnector } from './types'; import { httpServiceMock } from '@kbn/core/public/mocks'; jest.mock('./api'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const getAppInfoMock = getAppInfo as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.test.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.test.tsx index 06956e640230..b4d204c117b5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.test.tsx @@ -7,13 +7,13 @@ import { renderHook } from '@testing-library/react-hooks'; -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionConnector } from '../../../../types'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { useGetChoices, UseGetChoices, UseGetChoicesProps } from './use_get_choices'; import { getChoices } from './api'; jest.mock('./api'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; const getChoicesMock = getChoices as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.tsx index e81e0efe56a4..e2f0117e78ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { ActionConnector } from '../../../../types'; +import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { getChoices } from './api'; import { Choice } from './types'; import * as i18n from './translations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/api.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/api.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/api.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/api.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/api.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/api.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/helpers.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/helpers.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/index.ts new file mode 100644 index 000000000000..0819c1b731ac --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getSwimlaneConnectorType } from './swimlane'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/logo.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/logo.tsx index 572ff62d52f8..c86f97bf93a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/mocks.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/mocks.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/mocks.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/mocks.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/index.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/index.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_connection.tsx similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_connection.tsx index 1dbb2f21a4fb..2a32a7cace74 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_connection.tsx @@ -11,8 +11,7 @@ import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { FormattedMessage } from '@kbn/i18n-react'; -import { PasswordField } from '../../../password_field'; -import { useKibana } from '../../../../../common/lib/kibana'; +import { PasswordField, useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from '../translations'; interface Props { @@ -73,7 +72,7 @@ const SwimlaneConnectionComponent: React.FunctionComponent = ({ readOnly target="_blank" > diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_fields.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_fields.tsx index 10afa07c65a1..4afb2eacc891 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/steps/swimlane_fields.tsx @@ -16,6 +16,7 @@ import { } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { ComboBoxField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { ButtonGroupField } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from '../translations'; import { MappingConfigurationKeys, @@ -23,7 +24,6 @@ import { SwimlaneFieldMappingConfig, } from '../types'; import { isRequiredField, isValidFieldForConnector } from '../helpers'; -import { ButtonGroupField } from '../../../button_group_field'; const SINGLE_SELECTION = { asPlainText: true }; const EMPTY_COMBO_BOX_ARRAY: Array> | undefined = []; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.test.tsx similarity index 55% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.test.tsx index 479496b57ba8..13b68388573a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.test.tsx @@ -5,26 +5,26 @@ * 2.0. */ -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; -const ACTION_TYPE_ID = '.swimlane'; -let actionTypeModel: ActionTypeModel; +const CONNECTOR_TYPE_ID = '.swimlane'; +let connectorTypeModel: ConnectorTypeModel; beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); if (getResult !== null) { - actionTypeModel = getResult; + connectorTypeModel = getResult; } }); -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); }); }); @@ -37,7 +37,7 @@ describe('swimlane action params validation', () => { }, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.ruleName': [], 'subActionParams.incident.alertId': [], @@ -50,7 +50,7 @@ describe('swimlane action params validation', () => { subActionParams: { incident: {} }, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.ruleName': ['Rule name is required.'], 'subActionParams.incident.alertId': ['Alert ID is required.'], @@ -63,7 +63,7 @@ describe('swimlane action params validation', () => { subActionParams: {}, }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { 'subActionParams.incident.ruleName': [], 'subActionParams.incident.alertId': [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.tsx similarity index 85% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.tsx index 5a072a54efb6..594c2e83c891 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane.tsx @@ -7,24 +7,27 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; import { SwimlaneConfig, SwimlaneSecrets, SwimlaneActionParams } from './types'; export const SW_SELECT_MESSAGE_TEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText', + 'xpack.stackConnectors.components.swimlane.selectMessageText', { defaultMessage: 'Create record in Swimlane', } ); export const SW_ACTION_TYPE_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle', + 'xpack.stackConnectors.components.swimlane.connectorTypeTitle', { defaultMessage: 'Create Swimlane Record', } ); -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< SwimlaneConfig, SwimlaneSecrets, SwimlaneActionParams diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.test.tsx index bbe86a4fe7fe..c36b786863e3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.test.tsx @@ -11,12 +11,12 @@ import { act } from 'react-dom/test-utils'; import SwimlaneActionConnectorFields from './swimlane_connectors'; import { useGetApplication } from './use_get_application'; import { applicationFields, mappings } from './mocks'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import { waitFor } from '@testing-library/dom'; import userEvent from '@testing-library/user-event'; import { render } from '@testing-library/react'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); jest.mock('./use_get_application'); const useGetApplicationMock = useGetApplication as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.tsx index af59b9517b91..a7533f912ed4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_connectors.tsx @@ -15,8 +15,8 @@ import { EuiStepStatus, } from '@elastic/eui'; import { useFormContext, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { useKibana } from '../../../../common/lib/kibana'; -import { ActionConnectorFieldsProps } from '../../../../types'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import { SwimlaneFieldMappingConfig } from './types'; import { SwimlaneConnection, SwimlaneFields } from './steps'; import { useGetApplication } from './use_get_application'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_params.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_params.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_params.tsx similarity index 95% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_params.tsx index 098e6490f3fe..1faa8c8bbabf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/swimlane_params.tsx @@ -7,11 +7,13 @@ import React, { useCallback, useEffect, useRef, useMemo } from 'react'; import { EuiCallOut, EuiFormRow, EuiSpacer } from '@elastic/eui'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; -import { ActionParamsProps } from '../../../../types'; import { SwimlaneActionConnector, SwimlaneActionParams, SwimlaneConnectorType } from './types'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; const SwimlaneParamsFields: React.FunctionComponent> = ({ actionParams, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/translations.ts similarity index 53% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/translations.ts index 631e69e8e7a6..08d4a9fd198d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/translations.ts @@ -8,140 +8,137 @@ import { i18n } from '@kbn/i18n'; export const SW_REQUIRED_RULE_NAME = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName', + 'xpack.stackConnectors.components.swimlane.error.requiredRuleName', { defaultMessage: 'Rule name is required.', } ); export const SW_REQUIRED_APP_ID_TEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText', + 'xpack.stackConnectors.components.swimlane.error.requiredAppIdText', { defaultMessage: 'An App ID is required.', } ); export const SW_GET_APPLICATION_API_ERROR = (id: string | null) => - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage', - { - defaultMessage: 'Unable to get application with id {id}', - values: { id }, - } - ); + i18n.translate('xpack.stackConnectors.components.swimlane.unableToGetApplicationMessage', { + defaultMessage: 'Unable to get application with id {id}', + values: { id }, + }); export const SW_GET_APPLICATION_API_NO_FIELDS_ERROR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationFieldsMessage', + 'xpack.stackConnectors.components.swimlane.unableToGetApplicationFieldsMessage', { defaultMessage: 'Unable to get application fields', } ); export const SW_API_URL_TEXT_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel', + 'xpack.stackConnectors.components.swimlane.apiUrlTextFieldLabel', { defaultMessage: 'API Url', } ); export const SW_API_URL_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField', + 'xpack.stackConnectors.components.swimlane.invalidApiUrlTextField', { defaultMessage: 'URL is invalid.', } ); export const SW_APP_ID_TEXT_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.appIdTextFieldLabel', + 'xpack.stackConnectors.components.swimlane.appIdTextFieldLabel', { defaultMessage: 'Application ID', } ); export const SW_API_TOKEN_TEXT_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel', + 'xpack.stackConnectors.components.swimlane.apiTokenTextFieldLabel', { defaultMessage: 'API Token', } ); export const SW_MAPPING_TITLE_TEXT_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel', + 'xpack.stackConnectors.components.swimlane.mappingTitleTextFieldLabel', { defaultMessage: 'Configure Field Mappings', } ); export const SW_SEVERITY_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel', + 'xpack.stackConnectors.components.swimlane.severityFieldLabel', { defaultMessage: 'Severity', } ); export const SW_RULE_NAME_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel', + 'xpack.stackConnectors.components.swimlane.ruleNameFieldLabel', { defaultMessage: 'Rule name', } ); export const SW_ALERT_ID_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel', + 'xpack.stackConnectors.components.swimlane.alertIdFieldLabel', { defaultMessage: 'Alert ID', } ); export const SW_CASE_ID_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseIdFieldLabel', + 'xpack.stackConnectors.components.swimlane.caseIdFieldLabel', { defaultMessage: 'Case ID', } ); export const SW_CASE_NAME_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseNameFieldLabel', + 'xpack.stackConnectors.components.swimlane.caseNameFieldLabel', { defaultMessage: 'Case name', } ); export const SW_COMMENTS_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.commentsFieldLabel', + 'xpack.stackConnectors.components.swimlane.commentsFieldLabel', { defaultMessage: 'Comments', } ); export const SW_DESCRIPTION_FIELD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.descriptionFieldLabel', + 'xpack.stackConnectors.components.swimlane.descriptionFieldLabel', { defaultMessage: 'Description', } ); export const SW_CONFIGURE_CONNECTION_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.configureConnectionLabel', + 'xpack.stackConnectors.components.swimlane.configureConnectionLabel', { defaultMessage: 'Configure API Connection' } ); export const SW_CONNECTOR_TYPE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.connectorType', + 'xpack.stackConnectors.components.swimlane.connectorType', { defaultMessage: 'Connector Type', } ); export const EMPTY_MAPPING_WARNING_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle', + 'xpack.stackConnectors.components.swimlane.emptyMappingWarningTitle', { defaultMessage: 'This connector has missing field mappings', } ); export const EMPTY_MAPPING_WARNING_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc', + 'xpack.stackConnectors.components.swimlane.emptyMappingWarningDesc', { defaultMessage: 'This connector cannot be selected because it is missing the required alert field mappings. You can edit this connector to add required field mappings or select a connector of type Alerts.', @@ -149,63 +146,57 @@ export const EMPTY_MAPPING_WARNING_DESC = i18n.translate( ); export const SW_REQUIRED_SEVERITY = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity', + 'xpack.stackConnectors.components.swimlane.error.requiredSeverity', { defaultMessage: 'Severity is required.', } ); export const SW_REQUIRED_CASE_NAME = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName', + 'xpack.stackConnectors.components.swimlane.error.requiredCaseName', { defaultMessage: 'Case name is required.', } ); export const SW_REQUIRED_CASE_ID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID', + 'xpack.stackConnectors.components.swimlane.error.requiredCaseID', { defaultMessage: 'Case ID is required.', } ); export const SW_REQUIRED_COMMENTS = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments', + 'xpack.stackConnectors.components.swimlane.error.requiredComments', { defaultMessage: 'Comments are required.', } ); export const SW_REQUIRED_DESCRIPTION = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription', + 'xpack.stackConnectors.components.swimlane.error.requiredDescription', { defaultMessage: 'Description is required.', } ); export const SW_REQUIRED_ALERT_ID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID', + 'xpack.stackConnectors.components.swimlane.error.requiredAlertID', { defaultMessage: 'Alert ID is required.', } ); -export const SW_BACK = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep', - { - defaultMessage: 'Back', - } -); +export const SW_BACK = i18n.translate('xpack.stackConnectors.components.swimlane.prevStep', { + defaultMessage: 'Back', +}); -export const SW_NEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep', - { - defaultMessage: 'Next', - } -); +export const SW_NEXT = i18n.translate('xpack.stackConnectors.components.swimlane.nextStep', { + defaultMessage: 'Next', +}); export const SW_FIELDS_BUTTON_HELP_TEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText', + 'xpack.stackConnectors.components.swimlane.nextStepHelpText', { defaultMessage: 'If field mappings are not configured, Swimlane connector type will be set to all.', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/types.ts similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/types.ts index cf77b094e5a1..398a63164f9e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/types.ts @@ -5,11 +5,11 @@ * 2.0. */ +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { ExecutorSubActionPushParams, MappingConfigType, -} from '@kbn/stack-connectors-plugin/server/connector_types/cases/swimlane/types'; -import { UserConfiguredActionConnector } from '../../../../types'; +} from '../../../../server/connector_types/cases/swimlane/types'; export type SwimlaneActionConnector = UserConfiguredActionConnector< SwimlaneConfig, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/use_get_application.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/use_get_application.test.tsx index f852d40ebef2..d56f0d0a1d7a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/use_get_application.test.tsx @@ -7,12 +7,12 @@ import { renderHook, act } from '@testing-library/react-hooks'; -import { useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'; import { getApplication } from './api'; import { useGetApplication, UseGetApplication } from './use_get_application'; jest.mock('./api'); -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; const getApplicationMock = getApplication as jest.Mock; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/use_get_application.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/swimlane/use_get_application.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts new file mode 100644 index 000000000000..0faa7732194c --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getXmattersConnectorType } from './xmatters'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/logo.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/logo.tsx index dad43f666ad0..438710f2516c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => ( { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.actionTypeTitle).toEqual('xMatters data'); + }); +}); + +describe('xmatters action params validation', () => { + test('action params validation succeeds when action params is valid', async () => { + const actionParams = { + alertActionGroupName: 'Small t-shirt', + signalId: 'c9437cab-6a5b-45e8-bc8a-f4a8af440e97', + ruleName: 'Test xMatters', + date: '2022-01-18T19:01:08.818Z', + severity: 'high', + spaceId: 'default', + tags: 'test1, test2', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { alertActionGroupName: [], signalId: [] }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.tsx similarity index 75% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.tsx index 9f2955479d52..2ad23f91b7c3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.tsx @@ -7,11 +7,14 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { XmattersActionParams, XmattersConfig, XmattersSecrets } from '../types'; -import { AlertProvidedActionVariables } from '../../../lib/action_variables'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { AlertProvidedActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { XmattersActionParams, XmattersConfig, XmattersSecrets } from '../../types'; -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< XmattersConfig, XmattersSecrets, XmattersActionParams @@ -19,14 +22,11 @@ export function getActionType(): ActionTypeModel< return { id: '.xmatters', iconClass: lazy(() => import('./logo')), - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText', - { - defaultMessage: 'Trigger an xMatters workflow.', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.xmatters.selectMessageText', { + defaultMessage: 'Trigger an xMatters workflow.', + }), actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle', + 'xpack.stackConnectors.components.xmatters.connectorTypeTitle', { defaultMessage: 'xMatters data', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.test.tsx index cef4c2c2bef8..be7a6dc0ebdb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import XmattersActionConnectorFields from './xmatters_connectors'; -import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../test_utils'; +import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../../lib/test_utils'; import userEvent from '@testing-library/user-event'; import { act } from 'react-dom/test-utils'; import { render } from '@testing-library/react'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.tsx similarity index 89% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.tsx index 247a700876d1..881ea4c2164e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.tsx @@ -16,12 +16,14 @@ import { useFormData, } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { ActionConnectorFieldsProps } from '../../../../types'; -import { XmattersAuthenticationType } from '../types'; -import { ButtonGroupField } from '../../button_group_field'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + ButtonGroupField, + HiddenField, + PasswordField, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { XmattersAuthenticationType } from '../../types'; import * as i18n from './translations'; -import { PasswordField } from '../../password_field'; -import { HiddenField } from '../../hidden_field'; const { emptyField, urlField } = fieldValidators; @@ -53,7 +55,7 @@ const XmattersUrlField: React.FC<{ path: string; readOnly: boolean }> = ({ path, label: i18n.URL_LABEL, helpText: ( ), @@ -99,7 +101,7 @@ const XmattersActionConnectorFields: React.FunctionComponent

@@ -133,7 +135,7 @@ const XmattersActionConnectorFields: React.FunctionComponent diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.test.tsx index 64c4b5ead81a..500ee77efbd0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { XmattersSeverityOptions } from '../types'; +import { XmattersSeverityOptions } from '../../types'; import XmattersParamsFields from './xmatters_params'; describe('XmattersParamsFields renders', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.tsx similarity index 68% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.tsx index f66583ef32dd..b97db2f349f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.tsx @@ -9,15 +9,15 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui'; import { isUndefined } from 'lodash'; -import { ActionParamsProps } from '../../../../types'; -import { XmattersActionParams } from '../types'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextFieldWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { XmattersActionParams } from '../../types'; const severityOptions = [ { value: 'critical', text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel', + 'xpack.stackConnectors.components.xmatters.severitySelectCriticalOptionLabel', { defaultMessage: 'Critical', } @@ -26,7 +26,7 @@ const severityOptions = [ { value: 'high', text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectHighOptionLabel', + 'xpack.stackConnectors.components.xmatters.severitySelectHighOptionLabel', { defaultMessage: 'High', } @@ -35,7 +35,7 @@ const severityOptions = [ { value: 'medium', text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMediumOptionLabel', + 'xpack.stackConnectors.components.xmatters.severitySelectMediumOptionLabel', { defaultMessage: 'Medium', } @@ -43,17 +43,14 @@ const severityOptions = [ }, { value: 'low', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectLowOptionLabel', - { - defaultMessage: 'Low', - } - ), + text: i18n.translate('xpack.stackConnectors.components.xmatters.severitySelectLowOptionLabel', { + defaultMessage: 'Low', + }), }, { value: 'minimal', text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMinimalOptionLabel', + 'xpack.stackConnectors.components.xmatters.severitySelectMinimalOptionLabel', { defaultMessage: 'Minimal', } @@ -91,12 +88,9 @@ const XmattersParamsFields: React.FunctionComponent ValidatedEmail[]; +} + +export function registerConnectorTypes({ + connectorTypeRegistry, + services, +}: { + connectorTypeRegistry: TriggersAndActionsUIPublicPluginSetup['actionTypeRegistry']; + services: RegistrationServices; +}) { + connectorTypeRegistry.register(getServerLogConnectorType()); + connectorTypeRegistry.register(getSlackConnectorType()); + connectorTypeRegistry.register(getEmailConnectorType(services)); + connectorTypeRegistry.register(getIndexConnectorType()); + connectorTypeRegistry.register(getPagerDutyConnectorType()); + connectorTypeRegistry.register(getSwimlaneConnectorType()); + connectorTypeRegistry.register(getCasesWebhookConnectorType()); + connectorTypeRegistry.register(getWebhookConnectorType()); + connectorTypeRegistry.register(getXmattersConnectorType()); + connectorTypeRegistry.register(getServiceNowITSMConnectorType()); + connectorTypeRegistry.register(getServiceNowITOMConnectorType()); + connectorTypeRegistry.register(getServiceNowSIRConnectorType()); + connectorTypeRegistry.register(getJiraConnectorType()); + connectorTypeRegistry.register(getResilientConnectorType()); + connectorTypeRegistry.register(getTeamsConnectorType()); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/extract_action_variable.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/extract_action_variable.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/extract_action_variable.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/extract_action_variable.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/rewrite_response_body.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/rewrite_response_body.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/rewrite_response_body.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/rewrite_response_body.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/test_utils.tsx similarity index 80% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/test_utils.tsx index c1c5eaefaa42..59a6d3808ff8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/test_utils.tsx @@ -6,7 +6,6 @@ */ import React, { useCallback } from 'react'; -import { ReactWrapper } from 'enzyme'; import { of } from 'rxjs'; import { I18nProvider } from '@kbn/i18n-react'; import { EuiButton } from '@elastic/eui'; @@ -16,12 +15,12 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { ConnectorServices } from '../../../types'; -import { TriggersAndActionsUiServices } from '../../..'; -import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -import { ConnectorFormSchema } from '../../sections/action_connector_form/types'; -import { ConnectorFormFieldsGlobal } from '../../sections/action_connector_form/connector_form_fields_global'; -import { ConnectorProvider } from '../../context/connector_context'; +import { ConnectorServices } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { TriggersAndActionsUiServices } from '@kbn/triggers-actions-ui-plugin/public'; +import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock'; +import { ConnectorFormSchema } from '@kbn/triggers-actions-ui-plugin/public/application/sections/action_connector_form/types'; +import { ConnectorFormFieldsGlobal } from '@kbn/triggers-actions-ui-plugin/public/application/sections/action_connector_form/connector_form_fields_global'; +import { ConnectorProvider } from '@kbn/triggers-actions-ui-plugin/public/application/context/connector_context'; interface FormTestProviderProps { children: React.ReactNode; @@ -81,16 +80,6 @@ const FormTestProviderComponent: React.FC = ({ ); }; -FormTestProviderComponent.displayName = 'FormTestProvider'; -export const FormTestProvider = React.memo(FormTestProviderComponent); - -export async function waitForComponentToPaint

(wrapper: ReactWrapper

, amount = 0) { - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, amount)); - wrapper.update(); - }); -} - export const waitForComponentToUpdate = async () => await act(async () => { return Promise.resolve(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/security/index.ts similarity index 80% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/security/index.ts index ee207a6d8e8a..1fec1c76430e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/security/index.ts @@ -4,5 +4,3 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -export { getActionType as getJiraActionType } from './jira'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/api.ts similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/api.ts index 913bda49fe53..3323d3527428 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/api.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/api.ts @@ -6,8 +6,8 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../../constants'; -import { EmailConfig } from '../types'; +import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../../../common'; +import { EmailConfig } from '../../types'; export async function getServiceConfig({ http, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.test.tsx similarity index 74% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.test.tsx index 9e61d84f5fb3..4e67bdabef99 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.test.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; import { getEmailServices } from './email'; import { ValidatedEmail, @@ -16,8 +16,8 @@ import { MustacheInEmailRegExp, } from '@kbn/actions-plugin/common'; -const ACTION_TYPE_ID = '.email'; -let actionTypeModel: ActionTypeModel; +const CONNECTOR_TYPE_ID = '.email'; +let connectorTypeModel: ConnectorTypeModel; const RegistrationServices = { validateEmailAddresses: validateEmails, @@ -45,18 +45,18 @@ beforeEach(() => { }); beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: RegistrationServices }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: RegistrationServices }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); if (getResult !== null) { - actionTypeModel = getResult; + connectorTypeModel = getResult; } }); -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('email'); +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('email'); }); }); @@ -82,7 +82,7 @@ describe('action params validation', () => { subject: 'test', }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { to: [], cc: [], @@ -101,7 +101,7 @@ describe('action params validation', () => { subject: 'test', }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { to: ['Email address invalid.com is not valid.'], cc: ['Email address bob@notallowed.com is not allowed.'], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.tsx similarity index 67% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.tsx index b44d13fb02ec..73330de39be7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email.tsx @@ -10,63 +10,48 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSelectOption } from '@elastic/eui'; import { InvalidEmailReason } from '@kbn/actions-plugin/common'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { EmailActionParams, EmailConfig, EmailSecrets } from '../types'; -import { RegistrationServices } from '..'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { EmailActionParams, EmailConfig, EmailSecrets } from '../../types'; +import { RegistrationServices } from '../..'; const emailServices: EuiSelectOption[] = [ { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel', - { - defaultMessage: 'Gmail', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.gmailServerTypeLabel', { + defaultMessage: 'Gmail', + }), value: 'gmail', }, { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel', - { - defaultMessage: 'Outlook', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.outlookServerTypeLabel', { + defaultMessage: 'Outlook', + }), value: 'outlook365', }, { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.amazonSesServerTypeLabel', - { - defaultMessage: 'Amazon SES', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.amazonSesServerTypeLabel', { + defaultMessage: 'Amazon SES', + }), value: 'ses', }, { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.elasticCloudServerTypeLabel', - { - defaultMessage: 'Elastic Cloud', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.elasticCloudServerTypeLabel', { + defaultMessage: 'Elastic Cloud', + }), value: 'elastic_cloud', }, { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.exchangeServerTypeLabel', - { - defaultMessage: 'MS Exchange Server', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.exchangeServerTypeLabel', { + defaultMessage: 'MS Exchange Server', + }), value: 'exchange_server', }, { - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel', - { - defaultMessage: 'Other', - } - ), + text: i18n.translate('xpack.stackConnectors.components.email.otherServerTypeLabel', { + defaultMessage: 'Other', + }), value: 'other', }, ]; @@ -77,24 +62,18 @@ export function getEmailServices(isCloudEnabled: boolean) { : emailServices.filter((service) => service.value !== 'elastic_cloud'); } -export function getActionType( +export function getConnectorType( services: RegistrationServices -): ActionTypeModel { +): ConnectorTypeModel { return { id: '.email', iconClass: 'email', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText', - { - defaultMessage: 'Send email from your server.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle', - { - defaultMessage: 'Send to email', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.email.selectMessageText', { + defaultMessage: 'Send email from your server.', + }), + actionTypeTitle: i18n.translate('xpack.stackConnectors.components.email.connectorTypeTitle', { + defaultMessage: 'Send to email', + }), validateParams: async ( actionParams: EmailActionParams ): Promise> => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.test.tsx index b06340c7a180..ac9ba471b244 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.test.tsx @@ -9,7 +9,7 @@ import React, { Suspense } from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import EmailActionConnectorFields from './email_connector'; import * as hooks from './use_email_config'; import { @@ -17,9 +17,9 @@ import { ConnectorFormTestProvider, createAppMockRenderer, waitForComponentToUpdate, -} from '../test_utils'; +} from '../../lib/test_utils'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; describe('EmailActionConnectorFields', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.tsx similarity index 94% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.tsx index 213d30ff4e5e..502dc1d82dd1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_connector.tsx @@ -11,7 +11,6 @@ import { EuiFlexItem, EuiFlexGroup, EuiTitle, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiLink } from '@elastic/eui'; import { InvalidEmailReason } from '@kbn/actions-plugin/common'; -import { AdditionalEmailServices } from '@kbn/stack-connectors-plugin/common'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public'; import { @@ -26,13 +25,16 @@ import { TextField, ToggleField, } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { ActionConnectorFieldsProps } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + PasswordField, + useConnectorContext, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { AdditionalEmailServices } from '../../../../common'; import { getEmailServices } from './email'; import { useEmailConfig } from './use_email_config'; -import { PasswordField } from '../../password_field'; import * as i18n from './translations'; -import { useConnectorContext } from '../../../context/use_connector_context'; const { emptyField } = fieldValidators; @@ -50,7 +52,7 @@ const getEmailConfig = ( helpText: ( @@ -254,7 +256,7 @@ export const EmailActionConnectorFields: React.FunctionComponent

diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_params.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_params.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_params.tsx similarity index 85% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_params.tsx index 0f894e011d3e..9e5f1b06ad92 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/email_params.tsx @@ -9,10 +9,12 @@ import React, { useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiComboBox, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ActionParamsProps } from '../../../../types'; -import { EmailActionParams } from '../types'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { EmailActionParams } from '../../types'; export const EmailParamsFields = ({ actionParams, @@ -59,12 +61,9 @@ export const EmailParamsFields = ({ fullWidth error={errors.to} isInvalid={isToInvalid} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel', - { - defaultMessage: 'To', - } - )} + label={i18n.translate('xpack.stackConnectors.components.email.recipientTextFieldLabel', { + defaultMessage: 'To', + })} labelAppend={ <> @@ -72,7 +71,7 @@ export const EmailParamsFields = ({ setAddCC(true)}> ) : null} @@ -80,7 +79,7 @@ export const EmailParamsFields = ({ setAddBCC(true)}> ) : null} @@ -125,7 +124,7 @@ export const EmailParamsFields = ({ isInvalid={isCCInvalid} isDisabled={isDisabled} label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel', + 'xpack.stackConnectors.components.email.recipientCopyTextFieldLabel', { defaultMessage: 'Cc', } @@ -167,7 +166,7 @@ export const EmailParamsFields = ({ error={errors.bcc} isInvalid={isBCCInvalid} label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel', + 'xpack.stackConnectors.components.email.recipientBccTextFieldLabel', { defaultMessage: 'Bcc', } @@ -209,12 +208,9 @@ export const EmailParamsFields = ({ fullWidth error={errors.subject} isInvalid={isSubjectInvalid} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel', - { - defaultMessage: 'Subject', - } - )} + label={i18n.translate('xpack.stackConnectors.components.email.subjectTextFieldLabel', { + defaultMessage: 'Subject', + })} > { const actionConnector = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/exchange_form.tsx similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/exchange_form.tsx index 41f82b3a32bf..c38eebcf44fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/exchange_form.tsx @@ -11,9 +11,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { useKibana } from '../../../../common/lib/kibana'; +import { PasswordField, useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; -import { PasswordField } from '../../password_field'; const { emptyField } = fieldValidators; @@ -36,7 +35,7 @@ const ExchangeFormFields: React.FC = ({ readOnly }) => helpText: ( @@ -63,7 +62,7 @@ const ExchangeFormFields: React.FC = ({ readOnly }) => helpText: ( @@ -94,7 +93,7 @@ const ExchangeFormFields: React.FC = ({ readOnly }) => target="_blank" > diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/index.ts similarity index 78% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/index.ts index 2990773856ac..42fa6d59fefd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getActionType as getSlackActionType } from './slack'; +export { getConnectorType as getEmailConnectorType } from './email'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/translations.ts similarity index 54% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/translations.ts index 65fc2bdb542e..c21418f34909 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/translations.ts @@ -8,171 +8,168 @@ import { i18n } from '@kbn/i18n'; export const USERNAME_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel', + 'xpack.stackConnectors.components.email.userTextFieldLabel', { defaultMessage: 'Username', } ); export const PASSWORD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel', + 'xpack.stackConnectors.components.email.passwordFieldLabel', { defaultMessage: 'Password', } ); export const FROM_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel', + 'xpack.stackConnectors.components.email.fromTextFieldLabel', { defaultMessage: 'Sender', } ); export const SERVICE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.serviceTextFieldLabel', + 'xpack.stackConnectors.components.email.serviceTextFieldLabel', { defaultMessage: 'Service', } ); export const TENANT_ID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.tenantIdFieldLabel', + 'xpack.stackConnectors.components.email.tenantIdFieldLabel', { defaultMessage: 'Tenant ID', } ); export const CLIENT_ID_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientIdFieldLabel', + 'xpack.stackConnectors.components.email.clientIdFieldLabel', { defaultMessage: 'Client ID', } ); export const CLIENT_SECRET_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientSecretTextFieldLabel', + 'xpack.stackConnectors.components.email.clientSecretTextFieldLabel', { defaultMessage: 'Client Secret', } ); export const HOST_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel', + 'xpack.stackConnectors.components.email.hostTextFieldLabel', { defaultMessage: 'Host', } ); export const PORT_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel', + 'xpack.stackConnectors.components.email.portTextFieldLabel', { defaultMessage: 'Port', } ); export const SECURE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.secureSwitchLabel', + 'xpack.stackConnectors.components.email.secureSwitchLabel', { defaultMessage: 'Secure', } ); export const HAS_AUTH_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel', + 'xpack.stackConnectors.components.email.hasAuthSwitchLabel', { defaultMessage: 'Require authentication for this server', } ); export const SENDER_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText', + 'xpack.stackConnectors.components.email.error.requiredFromText', { defaultMessage: 'Sender is required.', } ); export const CLIENT_ID_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText', + 'xpack.stackConnectors.components.email.error.requiredClientIdText', { defaultMessage: 'Client ID is required.', } ); export const TENANT_ID_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText', + 'xpack.stackConnectors.components.email.error.requiredTenantIdText', { defaultMessage: 'Tenant ID is required.', } ); export const PORT_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText', + 'xpack.stackConnectors.components.email.error.requiredPortText', { defaultMessage: 'Port is required.', } ); export const PORT_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.invalidPortText', + 'xpack.stackConnectors.components.email.error.invalidPortText', { defaultMessage: 'Port is invalid.', } ); export const SERVICE_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText', + 'xpack.stackConnectors.components.email.error.requiredServiceText', { defaultMessage: 'Service is required.', } ); export const HOST_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText', + 'xpack.stackConnectors.components.email.error.requiredHostText', { defaultMessage: 'Host is required.', } ); export const USERNAME_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText', + 'xpack.stackConnectors.components.email.error.requiredAuthUserNameText', { defaultMessage: 'Username is required.', } ); export const TO_CC_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText', + 'xpack.stackConnectors.components.email.error.requiredEntryText', { defaultMessage: 'No To, Cc, or Bcc entry. At least one entry is required.', } ); export const MESSAGE_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText', + 'xpack.stackConnectors.components.email.error.requiredMessageText', { defaultMessage: 'Message is required.', } ); export const SUBJECT_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText', + 'xpack.stackConnectors.components.email.error.requiredSubjectText', { defaultMessage: 'Subject is required.', } ); export function getInvalidEmailAddress(email: string) { - return i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.invalidEmail', - { - defaultMessage: 'Email address {email} is not valid.', - values: { email }, - } - ); + return i18n.translate('xpack.stackConnectors.components.email.error.invalidEmail', { + defaultMessage: 'Email address {email} is not valid.', + values: { email }, + }); } export function getNotAllowedEmailAddress(email: string) { - return i18n.translate('xpack.triggersActionsUI.components.builtinActionTypes.error.notAllowed', { + return i18n.translate('xpack.stackConnectors.components.email.error.notAllowed', { defaultMessage: 'Email address {email} is not allowed.', values: { email }, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/use_email_config.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/use_email_config.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/use_email_config.ts similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/email/use_email_config.ts index fc0221227783..c93715e34ef8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/email/use_email_config.ts @@ -8,9 +8,9 @@ import { isEmpty } from 'lodash'; import { useCallback, useEffect, useRef, useState } from 'react'; import { HttpSetup, IToasts } from '@kbn/core/public'; -import { AdditionalEmailServices } from '@kbn/stack-connectors-plugin/common'; import { i18n } from '@kbn/i18n'; -import { EmailConfig } from '../types'; +import { AdditionalEmailServices } from '../../../../common'; +import { EmailConfig } from '../../types'; import { getServiceConfig } from './api'; interface Props { @@ -73,7 +73,7 @@ export function useEmailConfig({ http, toasts }: Props): UseEmailConfigReturnVal toasts.addDanger( error.body?.message ?? i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.updateErrorNotificationText', + 'xpack.stackConnectors.components.email.updateErrorNotificationText', { defaultMessage: 'Cannot get service configuration' } ) ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.test.tsx similarity index 61% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.test.tsx index ccf90cb91d83..f2e927fa42f3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.test.tsx @@ -5,34 +5,34 @@ * 2.0. */ -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; -const ACTION_TYPE_ID = '.index'; -let actionTypeModel: ActionTypeModel; +const CONNECTOR_TYPE_ID = '.index'; +let connectorTypeModel: ConnectorTypeModel; beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); if (getResult !== null) { - actionTypeModel = getResult; + connectorTypeModel = getResult; } }); -describe('actionTypeRegistry.get() works', () => { - test('action type .index is registered', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('indexOpen'); +describe('connectorTypeRegistry.get() works', () => { + test('connector type .index is registered', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('indexOpen'); }); }); describe('action params validation', () => { test('action params validation succeeds when action params are valid', async () => { expect( - await actionTypeModel.validateParams({ + await connectorTypeModel.validateParams({ documents: [{ test: 1234 }], }) ).toEqual({ @@ -43,7 +43,7 @@ describe('action params validation', () => { }); expect( - await actionTypeModel.validateParams({ + await connectorTypeModel.validateParams({ documents: [{ test: 1234 }], indexOverride: 'kibana-alert-history-anything', }) @@ -56,7 +56,7 @@ describe('action params validation', () => { }); test('action params validation fails when action params are invalid', async () => { - expect(await actionTypeModel.validateParams({})).toEqual({ + expect(await connectorTypeModel.validateParams({})).toEqual({ errors: { documents: ['Document is required and should be a valid JSON object.'], indexOverride: [], @@ -64,7 +64,7 @@ describe('action params validation', () => { }); expect( - await actionTypeModel.validateParams({ + await connectorTypeModel.validateParams({ documents: [{}], }) ).toEqual({ @@ -75,7 +75,7 @@ describe('action params validation', () => { }); expect( - await actionTypeModel.validateParams({ + await connectorTypeModel.validateParams({ documents: [{}], indexOverride: 'kibana-alert-history-', }) @@ -87,7 +87,7 @@ describe('action params validation', () => { }); expect( - await actionTypeModel.validateParams({ + await connectorTypeModel.validateParams({ documents: [{}], indexOverride: 'this.is-a_string', }) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.tsx similarity index 59% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.tsx index 75666c1282da..8265af8a74ef 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index.tsx @@ -7,25 +7,23 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult, ALERT_HISTORY_PREFIX } from '../../../../types'; -import { EsIndexConfig, IndexActionParams } from '../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { ALERT_HISTORY_PREFIX } from '@kbn/triggers-actions-ui-plugin/public'; +import { EsIndexConfig, IndexActionParams } from '../../types'; -export function getActionType(): ActionTypeModel { +export function getConnectorType(): ConnectorTypeModel { return { id: '.index', iconClass: 'indexOpen', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText', - { - defaultMessage: 'Index data into Elasticsearch.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle', - { - defaultMessage: 'Index data', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.index.selectMessageText', { + defaultMessage: 'Index data into Elasticsearch.', + }), + actionTypeTitle: i18n.translate('xpack.stackConnectors.components.index.connectorTypeTitle', { + defaultMessage: 'Index data', + }), actionConnectorFields: lazy(() => import('./es_index_connector')), actionParamsFields: lazy(() => import('./es_index_params')), validateParams: async ( @@ -43,13 +41,10 @@ export function getActionType(): ActionTypeModel { const module = jest.requireActual('lodash'); @@ -24,7 +28,7 @@ jest.mock('lodash', () => { }; }); -jest.mock('../../../../common/index_controls', () => ({ +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/index_controls', () => ({ firstFieldOption: { text: 'Select a field', value: '', @@ -33,7 +37,9 @@ jest.mock('../../../../common/index_controls', () => ({ getIndexOptions: jest.fn(), })); -const { getIndexOptions } = jest.requireMock('../../../../common/index_controls'); +const { getIndexOptions } = jest.requireMock( + '@kbn/triggers-actions-ui-plugin/public/common/index_controls' +); getIndexOptions.mockResolvedValueOnce([ { @@ -45,7 +51,9 @@ getIndexOptions.mockResolvedValueOnce([ }, ]); -const { getFields } = jest.requireMock('../../../../common/index_controls'); +const { getFields } = jest.requireMock( + '@kbn/triggers-actions-ui-plugin/public/common/index_controls' +); async function setup(actionConnector: any) { const wrapper = mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_connector.tsx similarity index 87% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_connector.tsx index 12fb79bd2845..ec090c3c004d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_connector.tsx @@ -29,10 +29,14 @@ import { ToggleField, SelectField } from '@kbn/es-ui-shared-plugin/static/forms/ import { DocLinksStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { ActionConnectorFieldsProps } from '../../../../types'; -import { getTimeFieldOptions } from '../../../../common/lib/get_time_options'; -import { firstFieldOption, getFields, getIndexOptions } from '../../../../common/index_controls'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + firstFieldOption, + getFields, + getIndexOptions, + getTimeFieldOptions, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; import * as translations from './translations'; interface TimeFieldOptions { @@ -47,13 +51,13 @@ const getIndexConfig = (docLinks: DocLinksStart): FieldConfig => ({ helpText: ( <> @@ -123,7 +127,7 @@ const IndexActionConnectorFields: React.FunctionComponent @@ -159,7 +163,7 @@ const IndexActionConnectorFields: React.FunctionComponent } @@ -168,13 +172,13 @@ const IndexActionConnectorFields: React.FunctionComponent @@ -221,7 +225,7 @@ const IndexActionConnectorFields: React.FunctionComponent {' '} { const original = jest.requireActual(kibanaReactPath); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_params.tsx similarity index 84% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_params.tsx index 0601d0a9b128..7c6e5bc97cfb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/es_index_params.tsx @@ -17,16 +17,16 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; import { - ActionParamsProps, AlertHistoryEsIndexConnectorId, AlertHistoryDocumentTemplate, AlertHistoryDefaultIndexName, ALERT_HISTORY_PREFIX, -} from '../../../../types'; -import { IndexActionParams } from '../types'; -import { JsonEditorWithMessageVariables } from '../../json_editor_with_message_variables'; -import { useKibana } from '../../../../common/lib/kibana'; + JsonEditorWithMessageVariables, + useKibana, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { IndexActionParams } from '../../types'; export const IndexParamsFields = ({ actionParams, @@ -90,7 +90,7 @@ export const IndexParamsFields = ({ }; const documentsFieldLabel = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel', + 'xpack.stackConnectors.components.index.documentsFieldLabel', { defaultMessage: 'Document to index', } @@ -108,7 +108,7 @@ export const IndexParamsFields = ({ > @@ -127,17 +127,14 @@ export const IndexParamsFields = ({ (errors.indexOverride as string[]) && errors.indexOverride.length > 0 } - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndex', - { - defaultMessage: 'Elasticsearch index', - } - )} + label={i18n.translate('xpack.stackConnectors.components.index.preconfiguredIndex', { + defaultMessage: 'Elasticsearch index', + })} labelAppend={resetDefaultIndex} helpText={ <> @@ -146,7 +143,7 @@ export const IndexParamsFields = ({ target="_blank" > @@ -189,18 +186,15 @@ export const IndexParamsFields = ({ : documentToIndex } label={documentsFieldLabel} - aria-label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel', - { - defaultMessage: 'Code editor', - } - )} + aria-label={i18n.translate('xpack.stackConnectors.components.index.jsonDocAriaLabel', { + defaultMessage: 'Code editor', + })} errors={errors.documents as string[]} onDocumentsChange={onDocumentsChange} helpText={ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/index.ts similarity index 77% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/index.ts index 63e1475a115f..f3c7daa86fbf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getActionType as getCasesWebhookActionType } from './webhook'; +export { getConnectorType as getIndexConnectorType } from './es_index'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/translations.ts similarity index 64% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/translations.ts index d86824fd1813..3153d8418204 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/es_index/translations.ts @@ -8,49 +8,49 @@ import { i18n } from '@kbn/i18n'; export const INDEX_IS_NOT_VALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.notValidIndexText', + 'xpack.stackConnectors.components.index.error.notValidIndexText', { defaultMessage: 'Index is not valid.', } ); export const DOCUMENT_NOT_VALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson', + 'xpack.stackConnectors.components.index.error.requiredDocumentJson', { defaultMessage: 'Document is required and should be a valid JSON object.', } ); export const HISTORY_NOT_VALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix', + 'xpack.stackConnectors.components.index.error.badIndexOverrideSuffix', { defaultMessage: 'Alert history index must contain valid suffix.', } ); export const EXECUTION_TIME_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel', + 'xpack.stackConnectors.components.index.executionTimeFieldLabel', { defaultMessage: 'Time field', } ); export const SHOW_TIME_FIELD_TOGGLE_TOOLTIP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip', + 'xpack.stackConnectors.components.index.definedateFieldTooltip', { defaultMessage: `Set this time field to the time the document was indexed.`, } ); export const REFRESH_FIELD_TOGGLE_TOOLTIP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshTooltip', + 'xpack.stackConnectors.components.index.refreshTooltip', { defaultMessage: 'Refresh the affected shards to make this operation visible to search.', } ); export const INDEX_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesToQueryLabel', + 'xpack.stackConnectors.components.index.indicesToQueryLabel', { defaultMessage: 'Index', } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts new file mode 100644 index 000000000000..93d444d20204 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getEmailConnectorType } from './email'; +export { getIndexConnectorType } from './es_index'; +export { getPagerDutyConnectorType } from './pagerduty'; +export { getServerLogConnectorType } from './server_log'; +export { getSlackConnectorType } from './slack'; +export { getTeamsConnectorType } from './teams'; +export { getWebhookConnectorType } from './webhook'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/index.ts new file mode 100644 index 000000000000..48ee4b5288ec --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getPagerDutyConnectorType } from './pagerduty'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/logo.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/logo.tsx index ab991651a8a5..9383a4cf5d59 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => ( { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); if (getResult !== null) { - actionTypeModel = getResult; + connectorTypeModel = getResult; } }); -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.actionTypeTitle).toEqual('Send to PagerDuty'); +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.actionTypeTitle).toEqual('Send to PagerDuty'); }); }); @@ -43,7 +43,7 @@ describe('pagerduty action params validation', () => { class: 'test class', }; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { dedupKey: [], summary: [], @@ -67,7 +67,7 @@ describe('pagerduty action params validation', () => { const expected = [expect.stringMatching(/^Timestamp must be a valid date/)]; - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ errors: { dedupKey: [], summary: [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty.tsx similarity index 71% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty.tsx index 8c3961e6d711..edb1e9b71b84 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty.tsx @@ -8,17 +8,22 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { + AlertProvidedActionVariables, + hasMustacheTokens, +} from '@kbn/triggers-actions-ui-plugin/public'; import { PagerDutyConfig, PagerDutySecrets, PagerDutyActionParams, EventActionOptions, -} from '../types'; -import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; -import { AlertProvidedActionVariables } from '../../../lib/action_variables'; +} from '../../types'; -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< PagerDutyConfig, PagerDutySecrets, PagerDutyActionParams @@ -26,14 +31,11 @@ export function getActionType(): ActionTypeModel< return { id: '.pagerduty', iconClass: lazy(() => import('./logo')), - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText', - { - defaultMessage: 'Send an event in PagerDuty.', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.pagerDuty.selectMessageText', { + defaultMessage: 'Send an event in PagerDuty.', + }), actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle', + 'xpack.stackConnectors.components.pagerDuty.connectorTypeTitle', { defaultMessage: 'Send to PagerDuty', } @@ -66,17 +68,14 @@ export function getActionType(): ActionTypeModel< if (!moment(actionParams.timestamp).isValid()) { const { nowShortFormat, nowLongFormat } = getValidTimestampExamples(); errors.timestamp.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp', - { - defaultMessage: - 'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', - values: { - nowShortFormat, - nowLongFormat, - }, - } - ) + i18n.translate('xpack.stackConnectors.components.pagerDuty.error.invalidTimestamp', { + defaultMessage: + 'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', + values: { + nowShortFormat, + nowLongFormat, + }, + }) ); } } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.test.tsx similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.test.tsx index 1ccd7fba702f..184ae124cbc0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.test.tsx @@ -9,11 +9,11 @@ import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; import PagerDutyActionConnectorFields from './pagerduty_connectors'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('PagerDutyActionConnectorFields renders', () => { test('all connector fields is rendered', async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.tsx similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.tsx index 21f7443b6b54..13139306ea78 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_connectors.tsx @@ -13,8 +13,8 @@ import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hoo import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { DocLinksStart } from '@kbn/core/public'; -import { ActionConnectorFieldsProps } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; const { emptyField, urlField } = fieldValidators; @@ -44,7 +44,7 @@ const getRoutingKeyConfig = (docLinks: DocLinksStart): FieldConfig => ({ helpText: ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.test.tsx similarity index 98% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.test.tsx index 19f47166b272..7f2933587ebc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { EventActionOptions, SeverityActionOptions } from '../types'; +import { EventActionOptions, SeverityActionOptions } from '../../types'; import PagerDutyParamsFields from './pagerduty_params'; describe('PagerDutyParamsFields renders', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.tsx similarity index 76% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.tsx index 738f1ca74b1a..f9f9921d84ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/pagerduty/pagerduty_params.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isUndefined } from 'lodash'; -import { ActionParamsProps } from '../../../../types'; -import { PagerDutyActionParams } from '../types'; -import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextFieldWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { PagerDutyActionParams } from '../../types'; const PagerDutyParamsFields: React.FunctionComponent> = ({ actionParams, @@ -26,7 +26,7 @@ const PagerDutyParamsFields: React.FunctionComponent { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('logsApp'); + }); +}); + +describe('action params validation', () => { + test('action params validation succeeds when action params is valid', async () => { + const actionParams = { + message: 'test message', + level: 'trace', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { message: [] }, + }); + }); + + test('params validation fails when message is not valid', async () => { + const actionParams = { + message: '', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + message: ['Message is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log.tsx similarity index 63% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log.tsx index 4fe024f8861f..b1dadf0bf2dd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log.tsx @@ -7,21 +7,21 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { ServerLogActionParams } from '../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { ServerLogActionParams } from '../../types'; -export function getActionType(): ActionTypeModel { +export function getConnectorType(): ConnectorTypeModel { return { id: '.server-log', iconClass: 'logsApp', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText', - { - defaultMessage: 'Add a message to a Kibana log.', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.serverLog.selectMessageText', { + defaultMessage: 'Add a message to a Kibana log.', + }), actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle', + 'xpack.stackConnectors.components.serverLog.connectorTypeTitle', { defaultMessage: 'Send to Server log', } @@ -36,7 +36,7 @@ export function getActionType(): ActionTypeModel { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log_params.tsx similarity index 79% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log_params.tsx index b8f5b84eb471..d923e1165c3f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/server_log/server_log_params.tsx @@ -8,9 +8,9 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSelect, EuiFormRow } from '@elastic/eui'; -import { ActionParamsProps } from '../../../../types'; -import { ServerLogActionParams } from '../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { ServerLogActionParams } from '../../types'; export const ServerLogParamsFields: React.FunctionComponent< ActionParamsProps @@ -52,12 +52,9 @@ export const ServerLogParamsFields: React.FunctionComponent< diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/index.ts similarity index 78% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/index.ts index a1ca62d0cc86..05d27afff76f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getActionType as getEmailActionType } from './email'; +export { getConnectorType as getSlackConnectorType } from './slack'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.test.tsx new file mode 100644 index 000000000000..dc1f27ec3998 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const CONNECTOR_TYPE_ID = '.slack'; +let connectorTypeModel: ConnectorTypeModel; + +beforeAll(async () => { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('logoSlack'); + }); +}); + +describe('slack action params validation', () => { + test('if action params validation succeeds when action params is valid', async () => { + const actionParams = { + message: 'message {test}', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { message: [] }, + }); + }); + + test('params validation fails when message is not valid', async () => { + const actionParams = { + message: '', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + message: ['Message is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.tsx similarity index 59% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.tsx index 2252677084ba..a3a800b2036a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack.tsx @@ -7,25 +7,22 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { SlackActionParams, SlackSecrets } from '../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { SlackActionParams, SlackSecrets } from '../../types'; -export function getActionType(): ActionTypeModel { +export function getConnectorType(): ConnectorTypeModel { return { id: '.slack', iconClass: 'logoSlack', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText', - { - defaultMessage: 'Send a message to a Slack channel or user.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle', - { - defaultMessage: 'Send to Slack', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.slack.selectMessageText', { + defaultMessage: 'Send a message to a Slack channel or user.', + }), + actionTypeTitle: i18n.translate('xpack.stackConnectors.components.slack.connectorTypeTitle', { + defaultMessage: 'Send to Slack', + }), validateParams: async ( actionParams: SlackActionParams ): Promise> => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.test.tsx index 640bef0c2268..ac381ba07e3b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.test.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act, render } from '@testing-library/react'; import SlackActionFields from './slack_connectors'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('SlackActionFields renders', () => { test('all connector fields is rendered', async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.tsx similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.tsx index 0725096c5a71..188b8912fc39 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_connectors.tsx @@ -12,9 +12,8 @@ import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hoo import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { DocLinksStart } from '@kbn/core/public'; - -import { ActionConnectorFieldsProps } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; const { urlField } = fieldValidators; @@ -24,7 +23,7 @@ const getWebhookUrlConfig = (docLinks: DocLinksStart): FieldConfig => ({ helpText: ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_params.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_params.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_params.tsx similarity index 79% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_params.tsx index 59e10277cfe0..d5cd699caaae 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/slack_params.tsx @@ -7,9 +7,9 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionParamsProps } from '../../../../types'; -import { SlackActionParams } from '../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { SlackActionParams } from '../../types'; const SlackParamsFields: React.FunctionComponent> = ({ actionParams, @@ -43,12 +43,9 @@ const SlackParamsFields: React.FunctionComponent ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/translations.ts similarity index 67% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/slack/translations.ts index e7d37082b53f..7caed9ca0723 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/slack/translations.ts @@ -8,21 +8,21 @@ import { i18n } from '@kbn/i18n'; export const WEBHOOK_URL_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText', + 'xpack.stackConnectors.components.slack.error.invalidWebhookUrlText', { defaultMessage: 'Webhook URL is invalid.', } ); export const MESSAGE_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText', + 'xpack.stackConnectors.components.slack..error.requiredSlackMessageText', { defaultMessage: 'Message is required.', } ); export const WEBHOOK_URL_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel', + 'xpack.stackConnectors.components.slack.webhookUrlTextFieldLabel', { defaultMessage: 'Webhook URL', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/index.ts similarity index 78% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/index.ts index 419fb069aa8c..a64d07770b4d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getActionType as getIndexActionType } from './es_index'; +export { getConnectorType as getTeamsConnectorType } from './teams'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/logo.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/logo.tsx index 666cb8d85403..a7740ece4323 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/logo.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { LogoProps } from '../types'; +import { LogoProps } from '../../types'; const Logo = (props: LogoProps) => ( { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + }); +}); + +describe('teams action params validation', () => { + test('if action params validation succeeds when action params is valid', async () => { + const actionParams = { + message: 'message {test}', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { message: [] }, + }); + }); + + test('params validation fails when message is not valid', async () => { + const actionParams = { + message: '', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + message: ['Message is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams.tsx similarity index 59% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams.tsx index e9c286cdc1b5..560d647253a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams.tsx @@ -7,25 +7,22 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { TeamsActionParams, TeamsSecrets } from '../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { TeamsActionParams, TeamsSecrets } from '../../types'; -export function getActionType(): ActionTypeModel { +export function getConnectorType(): ConnectorTypeModel { return { id: '.teams', iconClass: lazy(() => import('./logo')), - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText', - { - defaultMessage: 'Send a message to a Microsoft Teams channel.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle', - { - defaultMessage: 'Send a message to a Microsoft Teams channel.', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.teams.selectMessageText', { + defaultMessage: 'Send a message to a Microsoft Teams channel.', + }), + actionTypeTitle: i18n.translate('xpack.stackConnectors.components.teams.connectorTypeTitle', { + defaultMessage: 'Send a message to a Microsoft Teams channel.', + }), validateParams: async ( actionParams: TeamsActionParams ): Promise> => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.test.tsx index a0a082d36a86..37078d7efd11 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act, render } from '@testing-library/react'; import TeamsActionFields from './teams_connectors'; -import { ConnectorFormTestProvider } from '../test_utils'; +import { ConnectorFormTestProvider } from '../../lib/test_utils'; import userEvent from '@testing-library/user-event'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('TeamsActionFields renders', () => { test('all connector fields are rendered', async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.tsx similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.tsx index 34e2e02a0611..7d989b9b04c6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_connectors.tsx @@ -12,8 +12,8 @@ import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hoo import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { DocLinksStart } from '@kbn/core/public'; -import { ActionConnectorFieldsProps } from '../../../../types'; -import { useKibana } from '../../../../common/lib/kibana'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; const { urlField } = fieldValidators; @@ -23,7 +23,7 @@ const getWebhookUrlConfig = (docLinks: DocLinksStart): FieldConfig => ({ helpText: ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.test.tsx similarity index 93% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.test.tsx index cf0bfe9db0e9..ac1228ac5fda 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import TeamsParamsFields from './teams_params'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); describe('TeamsParamsFields renders', () => { test('all params fields is rendered', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.tsx similarity index 74% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.tsx index 0aea576c10b3..5a4c9e85f583 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/teams_params.tsx @@ -7,9 +7,9 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionParamsProps } from '../../../../types'; -import { TeamsActionParams } from '../types'; -import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { TeamsActionParams } from '../../types'; const TeamsParamsFields: React.FunctionComponent> = ({ actionParams, @@ -34,12 +34,9 @@ const TeamsParamsFields: React.FunctionComponent ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/translations.ts similarity index 67% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/teams/translations.ts index 2bf4cae881f7..539e0867dc97 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/teams/translations.ts @@ -8,21 +8,21 @@ import { i18n } from '@kbn/i18n'; export const WEBHOOK_URL_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.webhookUrlTextLabel', + 'xpack.stackConnectors.components.teams.error.webhookUrlTextLabel', { defaultMessage: 'Webhook URL', } ); export const WEBHOOK_URL_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText', + 'xpack.stackConnectors.components.teams.error.invalidWebhookUrlText', { defaultMessage: 'Webhook URL is invalid.', } ); export const MESSAGE_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText', + 'xpack.stackConnectors.components.teams.error.requiredMessageText', { defaultMessage: 'Message is required.', } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/index.ts new file mode 100644 index 000000000000..dd5934986c84 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getWebhookConnectorType } from './webhook'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/translations.ts similarity index 55% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/translations.ts index 27a7d08b8c76..7095c91729f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/translations.ts @@ -8,98 +8,98 @@ import { i18n } from '@kbn/i18n'; export const METHOD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel', + 'xpack.stackConnectors.components.webhook.methodTextFieldLabel', { defaultMessage: 'Method', } ); export const HAS_AUTH_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel', + 'xpack.stackConnectors.components.webhook.hasAuthSwitchLabel', { defaultMessage: 'Require authentication for this webhook', } ); export const URL_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel', + 'xpack.stackConnectors.components.webhook.urlTextFieldLabel', { defaultMessage: 'URL', } ); export const USERNAME_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel', + 'xpack.stackConnectors.components.webhook.userTextFieldLabel', { defaultMessage: 'Username', } ); export const PASSWORD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel', + 'xpack.stackConnectors.components.webhook.passwordTextFieldLabel', { defaultMessage: 'Password', } ); export const ADD_HEADERS_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch', + 'xpack.stackConnectors.components.webhook.viewHeadersSwitch', { defaultMessage: 'Add HTTP header', } ); export const HEADER_KEY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerKeyTextFieldLabel', + 'xpack.stackConnectors.components.webhook.headerKeyTextFieldLabel', { defaultMessage: 'Key', } ); export const REMOVE_ITEM_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.removeHeaderIconLabel', + 'xpack.stackConnectors.components.webhook.removeHeaderIconLabel', { defaultMessage: 'Key', } ); export const ADD_HEADER_BTN = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButtonLabel', + 'xpack.stackConnectors.components.webhook.addHeaderButtonLabel', { defaultMessage: 'Add header', } ); export const HEADER_VALUE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerValueTextFieldLabel', + 'xpack.stackConnectors.components.webhook.headerValueTextFieldLabel', { defaultMessage: 'Value', } ); export const URL_INVALID = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField', + 'xpack.stackConnectors.components.webhook.error.invalidUrlTextField', { defaultMessage: 'URL is invalid.', } ); export const METHOD_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText', + 'xpack.stackConnectors.components.webhook.error.requiredMethodText', { defaultMessage: 'Method is required.', } ); export const USERNAME_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText', + 'xpack.stackConnectors.components.webhook.error.requiredAuthUserNameText', { defaultMessage: 'Username is required.', } ); export const BODY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText', + 'xpack.stackConnectors.components.webhook.error.requiredWebhookBodyText', { defaultMessage: 'Body is required.', } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.test.tsx new file mode 100644 index 000000000000..d24e1e865e17 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const CONNECTOR_TYPE_ID = '.webhook'; +let connectorTypeModel: ConnectorTypeModel; + +beforeAll(() => { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + expect(connectorTypeModel.iconClass).toEqual('logoWebhook'); + }); +}); + +describe('webhook action params validation', () => { + test('action params validation succeeds when action params is valid', async () => { + const actionParams = { + body: 'message {test}', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { body: [] }, + }); + }); + + test('params validation fails when body is not valid', async () => { + const actionParams = { + body: '', + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + body: ['Body is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.tsx similarity index 67% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.tsx index 5ee08cc02700..9740038f85a6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook.tsx @@ -7,10 +7,13 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionTypeModel, GenericValidationResult } from '../../../../types'; -import { WebhookActionParams, WebhookConfig, WebhookSecrets } from '../types'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { WebhookActionParams, WebhookConfig, WebhookSecrets } from '../../types'; -export function getActionType(): ActionTypeModel< +export function getConnectorType(): ConnectorTypeModel< WebhookConfig, WebhookSecrets, WebhookActionParams @@ -18,18 +21,12 @@ export function getActionType(): ActionTypeModel< return { id: '.webhook', iconClass: 'logoWebhook', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText', - { - defaultMessage: 'Send a request to a web service.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle', - { - defaultMessage: 'Webhook data', - } - ), + selectMessage: i18n.translate('xpack.stackConnectors.components.webhook.selectMessageText', { + defaultMessage: 'Send a request to a web service.', + }), + actionTypeTitle: i18n.translate('xpack.stackConnectors.components.webhook.connectorTypeTitle', { + defaultMessage: 'Webhook data', + }), validateParams: async ( actionParams: WebhookActionParams ): Promise> => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.test.tsx similarity index 99% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.test.tsx index d3b933e9e2dc..8744f126e154 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import WebhookActionConnectorFields from './webhook_connectors'; -import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../test_utils'; +import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../../lib/test_utils'; import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.tsx index 7981f8fa4fa7..233d2eea4b11 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_connectors.tsx @@ -29,9 +29,9 @@ import { ToggleField, } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; -import { ActionConnectorFieldsProps } from '../../../../types'; +import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { PasswordField } from '@kbn/triggers-actions-ui-plugin/public'; import * as i18n from './translations'; -import { PasswordField } from '../../password_field'; const HTTP_VERBS = ['post', 'put']; const { emptyField, urlField } = fieldValidators; @@ -99,7 +99,7 @@ const WebhookActionConnectorFields: React.FunctionComponent

diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.test.tsx similarity index 88% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.test.tsx index 064d21b50e46..6cf29adfe89b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.test.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import WebhookParamsFields from './webhook_params'; -import { MockCodeEditor } from '../../../code_editor.mock'; +import { MockCodeEditor } from '@kbn/triggers-actions-ui-plugin/public/application/code_editor.mock'; -const kibanaReactPath = '../../../../../../../../src/plugins/kibana_react/public'; +const kibanaReactPath = '../../../../../../../src/plugins/kibana_react/public'; jest.mock(kibanaReactPath, () => { const original = jest.requireActual(kibanaReactPath); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.tsx similarity index 69% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.tsx index 2eab79b14f53..4a48027e8153 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/webhook/webhook_params.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionParamsProps } from '../../../../types'; -import { WebhookActionParams } from '../types'; -import { JsonEditorWithMessageVariables } from '../../json_editor_with_message_variables'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { JsonEditorWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { WebhookActionParams } from '../../types'; const WebhookParamsFields: React.FunctionComponent> = ({ actionParams, @@ -24,14 +24,11 @@ const WebhookParamsFields: React.FunctionComponent new StackConnectorsPublicPlugin(); diff --git a/x-pack/plugins/stack_connectors/public/mocks.ts b/x-pack/plugins/stack_connectors/public/mocks.ts new file mode 100644 index 000000000000..9e087c3cee6e --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/mocks.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 { ValidatedEmail } from '@kbn/actions-plugin/common'; +import { RegistrationServices } from './connector_types'; + +function validateEmailAddresses(addresses: string[]): ValidatedEmail[] { + return addresses.map((address) => ({ address, valid: true })); +} + +export const registrationServicesMock: RegistrationServices = { validateEmailAddresses }; diff --git a/x-pack/plugins/stack_connectors/public/plugin.ts b/x-pack/plugins/stack_connectors/public/plugin.ts new file mode 100644 index 000000000000..bc9d855a1430 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/plugin.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup, Plugin } from '@kbn/core/public'; +import { TriggersAndActionsUIPublicPluginSetup } from '@kbn/triggers-actions-ui-plugin/public'; +import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public'; +import { registerConnectorTypes } from './connector_types'; + +export type Setup = void; +export type Start = void; + +export interface StackConnectorsPublicSetupDeps { + triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; + actions: ActionsPublicPluginSetup; +} + +export class StackConnectorsPublicPlugin + implements Plugin +{ + public setup(core: CoreSetup, { triggersActionsUi, actions }: StackConnectorsPublicSetupDeps) { + registerConnectorTypes({ + connectorTypeRegistry: triggersActionsUi.actionTypeRegistry, + services: { + validateEmailAddresses: actions.validateEmailAddresses, + }, + }); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/plugins/stack_connectors/tsconfig.json b/x-pack/plugins/stack_connectors/tsconfig.json index 395dc5a65be8..1cf8281670d0 100644 --- a/x-pack/plugins/stack_connectors/tsconfig.json +++ b/x-pack/plugins/stack_connectors/tsconfig.json @@ -10,10 +10,12 @@ "server/**/*", // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 "server/**/*.json", - "common/**/*" + "common/**/*", + "public/**/*" ], "references": [ { "path": "../../../src/core/tsconfig.json" }, - { "path": "../actions/tsconfig.json" } + { "path": "../actions/tsconfig.json" }, + { "path": "../triggers_actions_ui/tsconfig.json" } ] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 04f32940b161..b2984037b030 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -6377,6 +6377,380 @@ "xpack.stackConnectors.xmatters.shouldNotHaveSecretsUrl": "secretsUrl ne doit pas être fournie lorsque usesBasic est vrai", "xpack.stackConnectors.xmatters.shouldNotHaveUsernamePassword": "Le nom d'utilisateur et le mot de passe ne doivent pas être fournis lorsque usesBasic est faux", "xpack.stackConnectors.xmatters.title": "xMatters", + "xpack.stackConnectors.components.casesWebhook.error.missingVariables": "{variableCount, plural, one {Variable obligatoire manquante} other {Variables obligatoires manquantes}} : {variables}", + "xpack.stackConnectors.components.index.preconfiguredIndexHelpText": "Les documents sont indexés dans l'index {alertHistoryIndex}. ", + "xpack.stackConnectors.components.jira.unableToGetIssueMessage": "Impossible d'obtenir le problème ayant l'ID {id}", + "xpack.stackConnectors.components.pagerDuty.error.invalidTimestamp": "L'horodatage doit être une date valide, telle que {nowShortFormat} ou {nowLongFormat}.", + "xpack.stackConnectors.components.serviceNow.apiInfoError": "Statut reçu : {status} lors de la tentative d'obtention d'informations sur l'application", + "xpack.stackConnectors.components.serviceNow.appInstallationInfo": "{update} {create} ", + "xpack.stackConnectors.components.serviceNow.updateSuccessToastTitle": "Connecteur {connectorName} mis à jour", + "xpack.stackConnectors.components.serviceNow.apiUrlHelpLabel": "Fournissez l'URL complète vers l'instance ServiceNow souhaitée. Si vous n'en avez pas, {instance}.", + "xpack.stackConnectors.components.serviceNow.appRunning": "L'application Elastic de l'app store ServiceNow doit être installée avant d'exécuter la mise à jour. {visitLink} pour installer l'application", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationMessage": "Impossible d'obtenir l'application avec l'ID {id}", + "xpack.stackConnectors.components.casesWebhook.commentsTextAreaFieldLabel": "Commentaires supplémentaires", + "xpack.stackConnectors.components.casesWebhook.descriptionTextAreaFieldLabel": "Description", + "xpack.stackConnectors.components.casesWebhook.tagsFieldLabel": "Balises", + "xpack.stackConnectors.components.casesWebhook.titleFieldLabel": "Résumé (requis)", + "xpack.stackConnectors.components.casesWebhook.addHeaderButton": "Ajouter", + "xpack.stackConnectors.components.casesWebhook.addVariable": "Ajouter une variable", + "xpack.stackConnectors.components.casesWebhook.authenticationLabel": "Authentification", + "xpack.stackConnectors.components.casesWebhook.caseCommentDesc": "Commentaire de cas Kibana", + "xpack.stackConnectors.components.casesWebhook.caseDescriptionDesc": "Description de cas Kibana", + "xpack.stackConnectors.components.casesWebhook.caseTagsDesc": "Balises de cas Kibana", + "xpack.stackConnectors.components.casesWebhook.caseTitleDesc": "Titre de cas Kibana", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonHelp": "Objet JSON pour créer un commentaire. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonTextFieldLabel": "Objet de création de commentaire", + "xpack.stackConnectors.components.casesWebhook.createCommentMethodTextFieldLabel": "Méthode de création de commentaire", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlHelp": "URL de l'API pour ajouter un commentaire au cas.", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlTextFieldLabel": "URL de création de commentaire", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonHelpText": "Objet JSON pour créer un cas. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonTextFieldLabel": "Objet de création de cas", + "xpack.stackConnectors.components.casesWebhook.createIncidentMethodTextFieldLabel": "Méthode de création de cas", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyHelpText": "Clé JSON dans la réponse de création de cas qui contient l'ID de cas externe", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyTextFieldLabel": "Clé de cas pour la réponse de création de cas", + "xpack.stackConnectors.components.casesWebhook.createIncidentUrlTextFieldLabel": "URL de création de cas", + "xpack.stackConnectors.components.casesWebhook.deleteHeaderButton": "Supprimer", + "xpack.stackConnectors.components.casesWebhook.docLink": "Configuration de Webhook - Connecteur de gestion des cas.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentIncidentText": "L'objet de création de commentaire doit être un JSON valide.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentUrlText": "L'URL de création de commentaire doit être au format URL.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentText": "L'objet de création de cas est requis et doit être un JSON valide.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateUrlText": "L'URL de création de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentUrlText": "L'URL d'obtention de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateIncidentText": "L'objet de mise à jour de cas est requis et doit être un JSON valide.", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateUrlText": "L'URL de mise à jour de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.externalIdDesc": "ID du système externe", + "xpack.stackConnectors.components.casesWebhook.externalTitleDesc": "Titre du système externe", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyHelp": "Clé JSON dans la réponse d’obtention de cas qui contient le titre de cas externe", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyTextFieldLabel": "Clé de titre externe pour la réponse d’obtention de cas", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlHelp": "URL d’API pour le JSON de détails d’obtention de cas provenant d’un système externe. Utilisez le sélecteur de variable pour ajouter à l’URL l'ID du système externe.", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlTextFieldLabel": "URL d’obtention de cas", + "xpack.stackConnectors.components.casesWebhook.hasAuthSwitchLabel": "Demander une authentification pour ce webhook", + "xpack.stackConnectors.components.casesWebhook.httpHeadersTitle": "En-têtes utilisés", + "xpack.stackConnectors.components.casesWebhook.jsonCodeEditorAriaLabel": "Éditeur de code", + "xpack.stackConnectors.components.casesWebhook.jsonFieldLabel": "JSON", + "xpack.stackConnectors.components.casesWebhook.keyTextFieldLabel": "Clé", + "xpack.stackConnectors.components.casesWebhook.next": "Suivant", + "xpack.stackConnectors.components.casesWebhook.passwordTextFieldLabel": "Mot de passe", + "xpack.stackConnectors.components.casesWebhook.previous": "Précédent", + "xpack.stackConnectors.components.casesWebhook.selectMessageText": "Envoyer une requête à un service web de gestion de cas.", + "xpack.stackConnectors.components.casesWebhook.step1": "Configurer le connecteur", + "xpack.stackConnectors.components.casesWebhook.step2": "Créer un cas", + "xpack.stackConnectors.components.casesWebhook.step2Description": "Définissez les champs pour créer le cas dans le système externe. Consultez la documentation de l'API de votre service pour savoir quels sont les champs obligatoires", + "xpack.stackConnectors.components.casesWebhook.step3": "Informations sur l’obtention de cas", + "xpack.stackConnectors.components.casesWebhook.step3Description": "Définissez les champs pour ajouter des commentaires au cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour la création de mises à jour dans les cas. Consultez la documentation de l'API de votre service pour savoir quels sont les champs obligatoires.", + "xpack.stackConnectors.components.casesWebhook.step4": "Commentaires et mises à jour", + "xpack.stackConnectors.components.casesWebhook.step4a": "Créer une mise à jour dans le cas", + "xpack.stackConnectors.components.casesWebhook.step4aDescription": "Définissez les champs pour créer des mises à jour du cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour l’ajout de commentaires aux cas.", + "xpack.stackConnectors.components.casesWebhook.step4b": "Ajouter un commentaire dans le cas", + "xpack.stackConnectors.components.casesWebhook.step4bDescription": "Définissez les champs pour ajouter des commentaires au cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour la création de mises à jour dans les cas.", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonHelpl": "Objet JSON pour mettre à jour le cas. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonTextFieldLabel": "Objet de mise à jour de cas", + "xpack.stackConnectors.components.casesWebhook.updateIncidentMethodTextFieldLabel": "Méthode de mise à jour de cas", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlHelp": "URL d’API pour mettre à jour le cas.", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlTextFieldLabel": "URL de mise à jour du cas", + "xpack.stackConnectors.components.casesWebhook.userTextFieldLabel": "Nom d'utilisateur", + "xpack.stackConnectors.components.casesWebhook.valueTextFieldLabel": "Valeur", + "xpack.stackConnectors.components.casesWebhook.viewHeadersSwitch": "Ajouter un en-tête HTTP", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlHelp": "URL pour voir le cas dans le système externe. Utilisez le sélecteur de variable pour ajouter à l’URL l'ID ou le titre du système externe.", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlTextFieldLabel": "URL de visualisation de cas externe", + "xpack.stackConnectors.components.serviceNow.requiredShortDescTextField": "Une brève description est requise.", + "xpack.stackConnectors.components.email.exchangeForm.clientIdHelpLabel": "Configurer l'ID client", + "xpack.stackConnectors.components.email.exchangeForm.clientSecretHelpLabel": "Configurer l'identifiant client secret", + "xpack.stackConnectors.components.email.exchangeForm.tenantIdHelpLabel": "Configurer l'ID locataire", + "xpack.stackConnectors.components.email.connectorTypeTitle": "Envoyer vers la messagerie électronique", + "xpack.stackConnectors.components.email.amazonSesServerTypeLabel": "Amazon SES", + "xpack.stackConnectors.components.email.configureAccountsHelpLabel": "Configurer les comptes de messagerie électronique", + "xpack.stackConnectors.components.email.elasticCloudServerTypeLabel": "Elastic Cloud", + "xpack.stackConnectors.components.email.exchangeServerTypeLabel": "MS Exchange Server", + "xpack.stackConnectors.components.email.gmailServerTypeLabel": "Gmail", + "xpack.stackConnectors.components.email.otherServerTypeLabel": "Autre", + "xpack.stackConnectors.components.email.outlookServerTypeLabel": "Outlook", + "xpack.stackConnectors.components.email.selectMessageText": "Envoyez un e-mail à partir de votre serveur.", + "xpack.stackConnectors.components.email.updateErrorNotificationText": "Impossible d’obtenir la configuration du service", + "xpack.stackConnectors.components.index.error.badIndexOverrideSuffix": "L'index d'historique d'alertes doit contenir un suffixe valide.", + "xpack.stackConnectors.components.email.error.invalidPortText": "Le port n'est pas valide.", + "xpack.stackConnectors.components.email.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", + "xpack.stackConnectors.components.email.error.requiredClientIdText": "L'ID client est requis.", + "xpack.stackConnectors.components.index.error.requiredDocumentJson": "Le document est requis et doit être un objet JSON valide.", + "xpack.stackConnectors.components.email.error.requiredEntryText": "Aucune entrée À, Cc ou Cci. Au moins une entrée est requise.", + "xpack.stackConnectors.components.email.error.requiredFromText": "L'expéditeur est requis.", + "xpack.stackConnectors.components.email.error.requiredHostText": "L'hôte est requis.", + "xpack.stackConnectors.components.email.error.requiredMessageText": "Le message est requis.", + "xpack.stackConnectors.components.email.error.requiredPortText": "Le port est requis.", + "xpack.stackConnectors.components.serverLog.error.requiredServerLogMessageText": "Le message est requis.", + "xpack.stackConnectors.components.email.error.requiredServiceText": "Le service est requis.", + "xpack.stackConnectors.components.email.error.requiredSubjectText": "Le sujet est requis.", + "xpack.stackConnectors.components.email.error.requiredTenantIdText": "L'ID locataire est requis.", + "xpack.stackConnectors.components.webhook.error.requiredWebhookBodyText": "Le corps est requis.", + "xpack.stackConnectors.components.casesWebhook.error.requiredWebhookSummaryText": "Le titre est requis.", + "xpack.stackConnectors.components.index.connectorTypeTitle": "Données d'index", + "xpack.stackConnectors.components.index.configureIndexHelpLabel": "Configuration du connecteur d'index.", + "xpack.stackConnectors.components.index.connectorSectionTitle": "Écrire dans l'index", + "xpack.stackConnectors.components.index.definedateFieldTooltip": "Définissez ce champ temporel sur l'heure à laquelle le document a été indexé.", + "xpack.stackConnectors.components.index.defineTimeFieldLabel": "Définissez l'heure pour chaque document", + "xpack.stackConnectors.components.index.documentsFieldLabel": "Document à indexer", + "xpack.stackConnectors.components.index.error.notValidIndexText": "L’index n’est pas valide.", + "xpack.stackConnectors.components.index.executionTimeFieldLabel": "Champ temporel", + "xpack.stackConnectors.components.index.howToBroadenSearchQueryDescription": "Utilisez le caractère * pour élargir votre recherche.", + "xpack.stackConnectors.components.index.indexDocumentHelpLabel": "Exemple de document d'index.", + "xpack.stackConnectors.components.index.indicesToQueryLabel": "Index", + "xpack.stackConnectors.components.index.jsonDocAriaLabel": "Éditeur de code", + "xpack.stackConnectors.components.index.preconfiguredIndex": "Index Elasticsearch", + "xpack.stackConnectors.components.index.preconfiguredIndexDocLink": "Affichez les documents.", + "xpack.stackConnectors.components.index.refreshLabel": "Actualiser l'index", + "xpack.stackConnectors.components.index.refreshTooltip": "Actualisez les partitions affectées pour rendre cette opération visible pour la recherche.", + "xpack.stackConnectors.components.index.selectMessageText": "Indexez les données dans Elasticsearch.", + "xpack.stackConnectors.components.jira.connectorTypeTitle": "Jira", + "xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "Token d'API", + "xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.jira.commentsTextAreaFieldLabel": "Commentaires supplémentaires", + "xpack.stackConnectors.components.jira.descriptionTextAreaFieldLabel": "Description", + "xpack.stackConnectors.components.jira.emailTextFieldLabel": "Adresse e-mail", + "xpack.stackConnectors.components.jira.impactSelectFieldLabel": "Étiquettes", + "xpack.stackConnectors.components.jira.labelsSpacesErrorMessage": "Les étiquettes ne peuvent pas contenir d'espaces.", + "xpack.stackConnectors.components.jira.parentIssueSearchLabel": "Problème parent", + "xpack.stackConnectors.components.jira.projectKey": "Clé de projet", + "xpack.stackConnectors.components.jira.requiredSummaryTextField": "Le résumé est requis.", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxAriaLabel": "Taper pour rechercher", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxPlaceholder": "Taper pour rechercher", + "xpack.stackConnectors.components.jira.searchIssuesLoading": "Chargement...", + "xpack.stackConnectors.components.jira.selectMessageText": "Créez un incident dans Jira.", + "xpack.stackConnectors.components.jira.severitySelectFieldLabel": "Priorité", + "xpack.stackConnectors.components.jira.summaryFieldLabel": "Résumé (requis)", + "xpack.stackConnectors.components.jira.unableToGetFieldsMessage": "Impossible d'obtenir les champs", + "xpack.stackConnectors.components.jira.unableToGetIssuesMessage": "Impossible d'obtenir les problèmes", + "xpack.stackConnectors.components.jira.unableToGetIssueTypesMessage": "Impossible d'obtenir les types d'erreurs", + "xpack.stackConnectors.components.jira.urgencySelectFieldLabel": "Type d'erreur", + "xpack.stackConnectors.components.pagerDuty.connectorTypeTitle": "Envoyer à PagerDuty", + "xpack.stackConnectors.components.pagerDuty.apiUrlInvalid": "URL d’API non valide", + "xpack.stackConnectors.components.pagerDuty.apiUrlTextFieldLabel": "URL de l'API (facultative)", + "xpack.stackConnectors.components.pagerDuty.classFieldLabel": "Classe (facultative)", + "xpack.stackConnectors.components.pagerDuty.componentTextFieldLabel": "Composant (facultatif)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextFieldLabel": "DedupKey (facultatif)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextRequiredFieldLabel": "DedupKey", + "xpack.stackConnectors.components.pagerDuty.error.requiredDedupKeyText": "DedupKey est requis lors de la résolution ou de la reconnaissance d'un incident.", + "xpack.stackConnectors.components.pagerDuty.error.requiredRoutingKeyText": "Une clé d'intégration / clé de routage est requise.", + "xpack.stackConnectors.components.pagerDuty.error.requiredSummaryText": "Le résumé est requis.", + "xpack.stackConnectors.components.pagerDuty.eventActionSelectFieldLabel": "Action de l'événement", + "xpack.stackConnectors.components.pagerDuty.eventSelectAcknowledgeOptionLabel": "Reconnaissance", + "xpack.stackConnectors.components.pagerDuty.eventSelectResolveOptionLabel": "Résoudre", + "xpack.stackConnectors.components.pagerDuty.eventSelectTriggerOptionLabel": "Déclencher", + "xpack.stackConnectors.components.pagerDuty.groupTextFieldLabel": "Regrouper (facultatif)", + "xpack.stackConnectors.components.pagerDuty.routingKeyNameHelpLabel": "Configurer un compte PagerDuty", + "xpack.stackConnectors.components.pagerDuty.routingKeyTextFieldLabel": "Clé d'intégration", + "xpack.stackConnectors.components.pagerDuty.selectMessageText": "Envoyez un événement dans PagerDuty.", + "xpack.stackConnectors.components.pagerDuty.severitySelectCriticalOptionLabel": "Critique", + "xpack.stackConnectors.components.pagerDuty.severitySelectErrorOptionLabel": "Erreur", + "xpack.stackConnectors.components.pagerDuty.severitySelectFieldLabel": "Sévérité (facultative)", + "xpack.stackConnectors.components.pagerDuty.severitySelectInfoOptionLabel": "Info", + "xpack.stackConnectors.components.pagerDuty.severitySelectWarningOptionLabel": "Avertissement", + "xpack.stackConnectors.components.pagerDuty.sourceTextFieldLabel": "Source (facultative)", + "xpack.stackConnectors.components.pagerDuty.summaryFieldLabel": "Résumé", + "xpack.stackConnectors.components.pagerDuty.timestampTextFieldLabel": "Horodatage (facultatif)", + "xpack.stackConnectors.components.resilient.connectorTypeTitle": "Résilient", + "xpack.stackConnectors.components.resilient.apiKeyId": "ID de clé d'API", + "xpack.stackConnectors.components.resilient.apiKeySecret": "Secret de clé d'API", + "xpack.stackConnectors.components.resilient.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.resilient.commentsTextAreaFieldLabel": "Commentaires supplémentaires", + "xpack.stackConnectors.components.resilient.descriptionTextAreaFieldLabel": "Description", + "xpack.stackConnectors.components.resilient.nameFieldLabel": "Nom (requis)", + "xpack.stackConnectors.components.resilient.orgId": "ID d'organisation", + "xpack.stackConnectors.components.resilient.requiredNameTextField": "Un nom est requis.", + "xpack.stackConnectors.components.resilient.selectMessageText": "Créez un incident dans IBM Resilient.", + "xpack.stackConnectors.components.resilient.severity": "Sévérité", + "xpack.stackConnectors.components.resilient.unableToGetIncidentTypesMessage": "Impossible d'obtenir les types d'incidents", + "xpack.stackConnectors.components.resilient.unableToGetSeverityMessage": "Impossible d'obtenir la sévérité", + "xpack.stackConnectors.components.resilient.urgencySelectFieldLabel": "Type d'incident", + "xpack.stackConnectors.components.serverLog.connectorTypeTitle": "Envoyer vers le log de serveur", + "xpack.stackConnectors.components.serverLog.logLevelFieldLabel": "Niveau", + "xpack.stackConnectors.components.serverLog.logMessageFieldLabel": "Message", + "xpack.stackConnectors.components.serverLog.selectMessageText": "Ajouter un message au log Kibana.", + "xpack.stackConnectors.components.serviceNow.apiUrlTextFieldLabel": "URL d'instance ServiceNow", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout": "Application Elastic ServiceNow non installée", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.content": "Veuillez vous rendre dans l'app store ServiceNow pour installer l'application", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.errorMessage": "Message d'erreur", + "xpack.stackConnectors.components.serviceNow.authenticationLabel": "Authentification", + "xpack.stackConnectors.components.serviceNow.cancelButtonText": "Annuler", + "xpack.stackConnectors.components.serviceNow.categoryTitle": "Catégorie", + "xpack.stackConnectors.components.serviceNow.clientIdTextFieldLabel": "ID client", + "xpack.stackConnectors.components.serviceNow.clientSecretTextFieldLabel": "Identifiant client secret", + "xpack.stackConnectors.components.serviceNow.commentsTextAreaFieldLabel": "Commentaires supplémentaires", + "xpack.stackConnectors.components.serviceNow.confirmButtonText": "Mettre à jour", + "xpack.stackConnectors.components.serviceNow.correlationDisplay": "Affichage de la corrélation (facultatif)", + "xpack.stackConnectors.components.serviceNow.correlationID": "ID corrélation (facultatif)", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutCreate": "ou créez-en un nouveau.", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutMigrate": "Supprimez ce connecteur", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutTitle": "Ce type de connecteur est déclassé", + "xpack.stackConnectors.components.serviceNow.descriptionTextAreaFieldLabel": "Description", + "xpack.stackConnectors.components.serviceNow.eventClassTextAreaFieldLabel": "Instance source", + "xpack.stackConnectors.components.serviceNow.fetchErrorMsg": "Impossible de récupérer. Vérifiez l'URL ou la configuration CORS de votre instance ServiceNow.", + "xpack.stackConnectors.components.serviceNow.impactSelectFieldLabel": "Impact", + "xpack.stackConnectors.components.serviceNow.installationCalloutTitle": "Pour utiliser ce connecteur, installez d'abord l'application Elastic à partir de l'app store ServiceNow.", + "xpack.stackConnectors.components.serviceNow.invalidApiUrlTextField": "L'URL n'est pas valide.", + "xpack.stackConnectors.components.serviceNow.keyIdTextFieldLabel": "ID de clé du vérificateur JWT", + "xpack.stackConnectors.components.serviceNow.messageKeyTextAreaFieldLabel": "Clé de message", + "xpack.stackConnectors.components.serviceNow.metricNameTextAreaFieldLabel": "Nom de l'indicateur", + "xpack.stackConnectors.components.serviceNow.nodeTextAreaFieldLabel": "Nœud", + "xpack.stackConnectors.components.serviceNow.passwordTextFieldLabel": "Mot de passe", + "xpack.stackConnectors.components.serviceNow.prioritySelectFieldLabel": "Priorité", + "xpack.stackConnectors.components.serviceNow.privateKeyPassLabelHelpText": "Il est requis uniquement si vous avez défini un mot de passe sur votre clé privée", + "xpack.stackConnectors.components.serviceNow.privateKeyPassTextFieldLabel": "Mot de passe de clé privée", + "xpack.stackConnectors.components.serviceNow.privateKeyTextFieldLabel": "Clé privée", + "xpack.stackConnectors.components.serviceNow.requiredClientIdTextField": "L'ID client est requis.", + "xpack.stackConnectors.components.serviceNow.requiredKeyIdTextField": "L'ID de clé du vérificateur JWT est requis.", + "xpack.stackConnectors.components.serviceNow.requiredPrivateKeyTextField": "La clé privée est requise.", + "xpack.stackConnectors.components.serviceNow.requiredSeverityTextField": "La sévérité est requise.", + "xpack.stackConnectors.components.serviceNow.requiredUserIdentifierTextField": "L'identifiant de l'utilisateur est requis.", + "xpack.stackConnectors.components.serviceNow.requiredUsernameTextField": "Le nom d'utilisateur est requis.", + "xpack.stackConnectors.components.serviceNow.resourceTextAreaFieldLabel": "Ressource", + "xpack.stackConnectors.components.serviceNow.setupDevInstance": "configurer une instance de développeur", + "xpack.stackConnectors.components.serviceNow.severityRequiredSelectFieldLabel": "Sévérité (requise)", + "xpack.stackConnectors.components.serviceNow.severitySelectFieldLabel": "Sévérité", + "xpack.stackConnectors.components.serviceNow.snInstanceLabel": "Instance ServiceNow", + "xpack.stackConnectors.components.serviceNow.sourceTextAreaFieldLabel": "Source", + "xpack.stackConnectors.components.serviceNow.subcategoryTitle": "Sous-catégorie", + "xpack.stackConnectors.components.serviceNow.title": "Incident", + "xpack.stackConnectors.components.serviceNow.titleFieldLabel": "Brève description (requise)", + "xpack.stackConnectors.components.serviceNow.typeTextAreaFieldLabel": "Type", + "xpack.stackConnectors.components.serviceNow.unableToGetChoicesMessage": "Impossible d'obtenir les choix", + "xpack.stackConnectors.components.serviceNow.unknown": "INCONNU", + "xpack.stackConnectors.components.serviceNow.updateCalloutText": "Le connecteur a été mis à jour.", + "xpack.stackConnectors.components.serviceNow.updateFormCredentialsTitle": "Fournir les informations d'authentification", + "xpack.stackConnectors.components.serviceNow.updateFormInstallTitle": "Installer l'application Elastic ServiceNow", + "xpack.stackConnectors.components.serviceNow.updateFormTitle": "Mettre à jour le connecteur ServiceNow", + "xpack.stackConnectors.components.serviceNow.updateFormUrlTitle": "Entrer votre URL d'instance ServiceNow", + "xpack.stackConnectors.components.serviceNow.urgencySelectFieldLabel": "Urgence", + "xpack.stackConnectors.components.serviceNow.useOAuth": "Utiliser l'authentification OAuth", + "xpack.stackConnectors.components.serviceNow.userEmailTextFieldLabel": "Identifiant de l'utilisateur", + "xpack.stackConnectors.components.serviceNow.usernameTextFieldLabel": "Nom d'utilisateur", + "xpack.stackConnectors.components.serviceNow.visitSNStore": "Visiter l'app store ServiceNow", + "xpack.stackConnectors.components.serviceNow.warningMessage": "Cette action mettra à jour toutes les instances de ce connecteur et ne pourra pas être annulée.", + "xpack.stackConnectors.components.serviceNow.correlationIDHelpLabel": "Identificateur pour les incidents de mise à jour", + "xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle": "ServiceNow ITOM", + "xpack.stackConnectors.components.serviceNowITOM.event": "Événement", + "xpack.stackConnectors.components.serviceNowITOM.selectMessageText": "Créez un événement dans ServiceNow ITOM.", + "xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle": "ServiceNow ITSM", + "xpack.stackConnectors.components.serviceNowITSM.selectMessageText": "Créez un incident dans ServiceNow ITSM.", + "xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle": "ServiceNow SecOps", + "xpack.stackConnectors.components.serviceNowSIR.selectMessageText": "Créez un incident dans ServiceNow SecOps.", + "xpack.stackConnectors.components.serviceNowSIR.title": "Incident de sécurité", + "xpack.stackConnectors.components.serviceNowSIR.correlationIDHelpLabel": "Identificateur pour les incidents de mise à jour", + "xpack.stackConnectors.components.slack.connectorTypeTitle": "Envoyer vers Slack", + "xpack.stackConnectors.components.slack.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.", + "xpack.stackConnectors.components.slack.messageTextAreaFieldLabel": "Message", + "xpack.stackConnectors.components.slack.selectMessageText": "Envoyez un message à un canal ou à un utilisateur Slack.", + "xpack.stackConnectors.components.slack.webhookUrlHelpLabel": "Créer une URL de webhook Slack", + "xpack.stackConnectors.components.slack.webhookUrlTextFieldLabel": "URL de webhook", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationFieldsMessage": "Impossible d'obtenir les champs de l'application", + "xpack.stackConnectors.components.swimlane.connectorTypeTitle": "Créer l'enregistrement Swimlane", + "xpack.stackConnectors.components.swimlane.alertIdFieldLabel": "ID de l'alerte", + "xpack.stackConnectors.components.swimlane.apiTokenNameHelpLabel": "Fournir un token d'API Swimlane", + "xpack.stackConnectors.components.swimlane.apiTokenTextFieldLabel": "Token d'API", + "xpack.stackConnectors.components.swimlane.apiUrlTextFieldLabel": "URL d'API", + "xpack.stackConnectors.components.swimlane.appIdTextFieldLabel": "ID d'application", + "xpack.stackConnectors.components.swimlane.caseIdFieldLabel": "ID de cas", + "xpack.stackConnectors.components.swimlane.caseNameFieldLabel": "Nom de cas", + "xpack.stackConnectors.components.swimlane.commentsFieldLabel": "Commentaires", + "xpack.stackConnectors.components.swimlane.configureConnectionLabel": "Configurer la connexion de l'API", + "xpack.stackConnectors.components.swimlane.connectorType": "Type de connecteur", + "xpack.stackConnectors.components.swimlane.descriptionFieldLabel": "Description", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningDesc": "Ce connecteur ne peut pas être sélectionné, car il ne possède pas les mappings de champs d'alerte requis. Vous pouvez modifier ce connecteur pour ajouter les mappings de champs requis ou sélectionner un connecteur de type Alertes.", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningTitle": "Ce connecteur ne possède pas de mappings de champs", + "xpack.stackConnectors.components.swimlane.error.requiredAlertID": "L'ID d'alerte est requis.", + "xpack.stackConnectors.components.swimlane.error.requiredAppIdText": "Un ID d'application est requis.", + "xpack.stackConnectors.components.swimlane.error.requiredCaseID": "L'ID de cas est requis.", + "xpack.stackConnectors.components.swimlane.error.requiredCaseName": "Le nom de cas est requis.", + "xpack.stackConnectors.components.swimlane.error.requiredComments": "Les commentaires sont requis.", + "xpack.stackConnectors.components.swimlane.error.requiredDescription": "La description est requise.", + "xpack.stackConnectors.components.swimlane.error.requiredRuleName": "Le nom de règle est requis.", + "xpack.stackConnectors.components.swimlane.error.requiredSeverity": "La sévérité est requise.", + "xpack.stackConnectors.components.swimlane.invalidApiUrlTextField": "L'URL n'est pas valide.", + "xpack.stackConnectors.components.swimlane.mappingTitleTextFieldLabel": "Configurer les mappings de champs", + "xpack.stackConnectors.components.swimlane.nextStep": "Suivant", + "xpack.stackConnectors.components.swimlane.nextStepHelpText": "Si les mappings de champs ne sont pas configurés, le type de connecteur Swimlane sera défini sur Tous.", + "xpack.stackConnectors.components.swimlane.prevStep": "Retour", + "xpack.stackConnectors.components.swimlane.ruleNameFieldLabel": "Nom de règle", + "xpack.stackConnectors.components.swimlane.selectMessageText": "Créer un enregistrement dans Swimlane", + "xpack.stackConnectors.components.swimlane.severityFieldLabel": "Sévérité", + "xpack.stackConnectors.components.teams.connectorTypeTitle": "Envoyer un message à un canal Microsoft Teams.", + "xpack.stackConnectors.components.teams.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.", + "xpack.stackConnectors.components.teams.error.requiredMessageText": "Le message est requis.", + "xpack.stackConnectors.components.teams.error.webhookUrlTextLabel": "URL de webhook", + "xpack.stackConnectors.components.teams.messageTextAreaFieldLabel": "Message", + "xpack.stackConnectors.components.teams.selectMessageText": "Envoyer un message à un canal Microsoft Teams.", + "xpack.stackConnectors.components.teams.webhookUrlHelpLabel": "Créer une URL de webhook Microsoft Teams", + "xpack.stackConnectors.components.webhook.connectorTypeTitle": "Données de webhook", + "xpack.stackConnectors.components.webhook.addHeaderButtonLabel": "Ajouter un en-tête", + "xpack.stackConnectors.components.webhook.authenticationLabel": "Authentification", + "xpack.stackConnectors.components.webhook.bodyCodeEditorAriaLabel": "Éditeur de code", + "xpack.stackConnectors.components.webhook.bodyFieldLabel": "Corps", + "xpack.stackConnectors.components.webhook.error.invalidUrlTextField": "L'URL n'est pas valide.", + "xpack.stackConnectors.components.webhook.hasAuthSwitchLabel": "Demander une authentification pour ce webhook", + "xpack.stackConnectors.components.webhook.headerKeyTextFieldLabel": "Clé", + "xpack.stackConnectors.components.webhook.headerValueTextFieldLabel": "Valeur", + "xpack.stackConnectors.components.webhook.methodTextFieldLabel": "Méthode", + "xpack.stackConnectors.components.webhook.passwordTextFieldLabel": "Mot de passe", + "xpack.stackConnectors.components.webhook.removeHeaderIconLabel": "Clé", + "xpack.stackConnectors.components.webhook.selectMessageText": "Envoyer une requête à un service Web.", + "xpack.stackConnectors.components.webhook.urlTextFieldLabel": "URL", + "xpack.stackConnectors.components.webhook.userTextFieldLabel": "Nom d'utilisateur", + "xpack.stackConnectors.components.webhook.viewHeadersSwitch": "Ajouter un en-tête HTTP", + "xpack.stackConnectors.components.xmatters.connectorTypeTitle": "Données xMatters", + "xpack.stackConnectors.components.xmatters.authenticationLabel": "Authentification", + "xpack.stackConnectors.components.xmatters.basicAuthButtonGroupLegend": "Authentification de base", + "xpack.stackConnectors.components.xmatters.basicAuthLabel": "Authentification de base", + "xpack.stackConnectors.components.xmatters.connectorSettingsLabel": "Sélectionnez la méthode d'authentification utilisée pour la configuration du déclencheur xMatters.", + "xpack.stackConnectors.components.xmatters.error.invalidUrlTextField": "L'URL n'est pas valide.", + "xpack.stackConnectors.components.xmatters.error.invalidUsernameTextField": "Nom d'utilisateur non valide.", + "xpack.stackConnectors.components.xmatters.error.requiredUrlText": "L'URL est requise.", + "xpack.stackConnectors.components.xmatters.initiationUrlHelpText": "Spécifiez l'URL xMatters complète.", + "xpack.stackConnectors.components.xmatters.passwordTextFieldLabel": "Mot de passe", + "xpack.stackConnectors.components.xmatters.selectMessageText": "Déclenchez un workflow xMatters.", + "xpack.stackConnectors.components.xmatters.severity": "Sévérité", + "xpack.stackConnectors.components.xmatters.severitySelectCriticalOptionLabel": "Critique", + "xpack.stackConnectors.components.xmatters.severitySelectHighOptionLabel": "Élevé", + "xpack.stackConnectors.components.xmatters.severitySelectLowOptionLabel": "Bas", + "xpack.stackConnectors.components.xmatters.severitySelectMediumOptionLabel": "Moyenne", + "xpack.stackConnectors.components.xmatters.severitySelectMinimalOptionLabel": "Minimale", + "xpack.stackConnectors.components.xmatters.tags": "Balises", + "xpack.stackConnectors.components.xmatters.urlAuthLabel": "Authentification de l'URL", + "xpack.stackConnectors.components.xmatters.urlLabel": "URL d'initiation", + "xpack.stackConnectors.components.xmatters.userCredsLabel": "Identifiants d'utilisateur", + "xpack.stackConnectors.components.xmatters.userTextFieldLabel": "Nom d'utilisateur", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningDesc": "Configurez les champs Create Comment URL et Create Comment Objects pour que le connecteur puisse partager les commentaires.", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningTitle": "Impossible de partager les commentaires du cas", + "xpack.stackConnectors.components.serviceNow.deprecatedTooltipTitle": "Connecteur déclassé", + "xpack.stackConnectors.components.casesWebhook.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentMethodText": "La méthode de création de commentaire est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentResponseKeyText": "La clé d’ID de cas pour la réponse de création de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateMethodText": "La méthode de création de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseCreatedKeyText": "La clé de date de création de la réponse d’obtention de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseExternalTitleKeyText": "La clé de titre du cas externe pour la réponse d’obtention de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseUpdatedKeyText": "La clé de date de mise à jour de la réponse d’obtention de cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentViewUrlKeyText": "L'URL de visualisation du cas est requise.", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateMethodText": "La méthode de mise à jour du cas est requise.", + "xpack.stackConnectors.components.webhook.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", + "xpack.stackConnectors.components.webhook.error.requiredMethodText": "La méthode est requise.", + "xpack.stackConnectors.components.email.addBccButton": "Cci", + "xpack.stackConnectors.components.email.addCcButton": "Cc", + "xpack.stackConnectors.components.email.authenticationLabel": "Authentification", + "xpack.stackConnectors.components.email.clientIdFieldLabel": "ID client", + "xpack.stackConnectors.components.email.clientSecretTextFieldLabel": "Identifiant client secret", + "xpack.stackConnectors.components.email.fromTextFieldLabel": "Expéditeur", + "xpack.stackConnectors.components.email.hasAuthSwitchLabel": "Demander une authentification pour ce serveur", + "xpack.stackConnectors.components.email.hostTextFieldLabel": "Hôte", + "xpack.stackConnectors.components.email.messageTextAreaFieldLabel": "Message", + "xpack.stackConnectors.components.email.passwordFieldLabel": "Mot de passe", + "xpack.stackConnectors.components.email.portTextFieldLabel": "Port", + "xpack.stackConnectors.components.email.recipientBccTextFieldLabel": "Cci", + "xpack.stackConnectors.components.email.recipientCopyTextFieldLabel": "Cc", + "xpack.stackConnectors.components.email.recipientTextFieldLabel": "À", + "xpack.stackConnectors.components.email.secureSwitchLabel": "Sécurisé", + "xpack.stackConnectors.components.email.serviceTextFieldLabel": "Service", + "xpack.stackConnectors.components.email.subjectTextFieldLabel": "Objet", + "xpack.stackConnectors.components.email.tenantIdFieldLabel": "ID locataire", + "xpack.stackConnectors.components.email.userTextFieldLabel": "Nom d'utilisateur", + "xpack.stackConnectors.components.index.resetDefaultIndexLabel": "Réinitialiser l'index par défaut", "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldCandidates": "{fieldCandidatesCount, plural, one {# candidat de champ identifié} other {# candidats de champs identifiés}}.", "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldValuePairs": "{fieldValuePairsCount, plural, one {# paire significative champ/valeur identifiée} other {# paires significatives champ/valeur identifiées}}.", "xpack.aiops.index.dataLoader.internalServerErrorMessage": "Erreur lors du chargement des données dans l'index {index}. {message}. La requête a peut-être expiré. Essayez d'utiliser un échantillon d'une taille inférieure ou de réduire la plage temporelle.", @@ -31829,20 +32203,7 @@ "xpack.triggersActionsUI.actionVariables.legacyTagsLabel": "Cet élément a été déclassé au profit de {variable}.", "xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "Ce connecteur requiert une licence {minimumLicenseRequired}.", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "Ce type de règle requiert une licence {minimumLicenseRequired}.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.missingVariables": "{variableCount, plural, one {Variable obligatoire manquante} other {Variables obligatoires manquantes}} : {variables}", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "L'index d'historique d'alertes doit commencer par \"{alertHistoryPrefix}\".", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidEmail": "L'adresse e-mail {email} n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.notAllowed": "L'adresse e-mail {email} n'est pas autorisée.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexHelpText": "Les documents sont indexés dans l'index {alertHistoryIndex}. ", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueMessage": "Impossible d'obtenir le problème ayant l'ID {id}", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "Les informations sensibles ne sont pas importées. Veuillez entrer {encryptedFieldsLength, plural, one {la valeur} other {les valeurs}} pour {encryptedFieldsLength, plural, one {le champ suivant} other {les champs suivants}} {secretFieldsLabel}.", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp": "L'horodatage doit être une date valide, telle que {nowShortFormat} ou {nowLongFormat}.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "Statut reçu : {status} lors de la tentative d'obtention d'informations sur l'application", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateSuccessToastTitle": "Connecteur {connectorName} mis à jour", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.apiUrlHelpLabel": "Fournissez l'URL complète vers l'instance ServiceNow souhaitée. Si vous n'en avez pas, {instance}.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.serviceNowAppRunning": "L'application Elastic de l'app store ServiceNow doit être installée avant d'exécuter la mise à jour. {visitLink} pour installer l'application", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "Impossible d'obtenir l'application avec l'ID {id}", "xpack.triggersActionsUI.components.buttonGroupField.error.requiredField": "{label} est obligatoire.", "xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "Impossible de supprimer {numErrors, number} {numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}", "xpack.triggersActionsUI.components.deleteSelectedIdsSuccessNotification.descriptionText": "Suppression de {numSuccesses, number} {numSuccesses, plural, one {{singleTitle}} other {{multipleTitle}}} effectuée", @@ -31998,340 +32359,6 @@ "xpack.triggersActionsUI.components.addMessageVariables.addRuleVariableTitle": "Ajouter une variable de règle", "xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton": "Ajouter une variable", "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "Une erreur s'est produite lors de la recherche des alertes", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.commentsTextAreaFieldLabel": "Commentaires supplémentaires", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.descriptionTextAreaFieldLabel": "Description", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.tagsFieldLabel": "Balises", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.titleFieldLabel": "Résumé (requis)", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.actionTypeTitle": "Webhook - Données de gestion des cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addHeaderButton": "Ajouter", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addVariable": "Ajouter une variable", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseCommentDesc": "Commentaire de cas Kibana", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseDescriptionDesc": "Description de cas Kibana", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTagsDesc": "Balises de cas Kibana", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTitleDesc": "Titre de cas Kibana", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonHelp": "Objet JSON pour créer un commentaire. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonTextFieldLabel": "Objet de création de commentaire", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentMethodTextFieldLabel": "Méthode de création de commentaire", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlHelp": "URL de l'API pour ajouter un commentaire au cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlTextFieldLabel": "URL de création de commentaire", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonHelpText": "Objet JSON pour créer un cas. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonTextFieldLabel": "Objet de création de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentMethodTextFieldLabel": "Méthode de création de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyHelpText": "Clé JSON dans la réponse de création de cas qui contient l'ID de cas externe", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyTextFieldLabel": "Clé de cas pour la réponse de création de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentUrlTextFieldLabel": "URL de création de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.deleteHeaderButton": "Supprimer", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.docLink": "Configuration de Webhook - Connecteur de gestion des cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentIncidentText": "L'objet de création de commentaire doit être un JSON valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentUrlText": "L'URL de création de commentaire doit être au format URL.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateIncidentText": "L'objet de création de cas est requis et doit être un JSON valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateUrlText": "L'URL de création de cas est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredGetIncidentUrlText": "L'URL d'obtention de cas est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateIncidentText": "L'objet de mise à jour de cas est requis et doit être un JSON valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateUrlText": "L'URL de mise à jour de cas est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalIdDesc": "ID du système externe", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalTitleDesc": "Titre du système externe", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyHelp": "Clé JSON dans la réponse d’obtention de cas qui contient le titre de cas externe", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyTextFieldLabel": "Clé de titre externe pour la réponse d’obtention de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlHelp": "URL d’API pour le JSON de détails d’obtention de cas provenant d’un système externe. Utilisez le sélecteur de variable pour ajouter à l’URL l'ID du système externe.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlTextFieldLabel": "URL d’obtention de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.hasAuthSwitchLabel": "Demander une authentification pour ce webhook", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.httpHeadersTitle": "En-têtes utilisés", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonCodeEditorAriaLabel": "Éditeur de code", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonFieldLabel": "JSON", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.keyTextFieldLabel": "Clé", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.next": "Suivant", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.passwordTextFieldLabel": "Mot de passe", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.previous": "Précédent", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.selectMessageText": "Envoyer une requête à un service web de gestion de cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step1": "Configurer le connecteur", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2": "Créer un cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2Description": "Définissez les champs pour créer le cas dans le système externe. Consultez la documentation de l'API de votre service pour savoir quels sont les champs obligatoires", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3": "Informations sur l’obtention de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3Description": "Définissez les champs pour ajouter des commentaires au cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour la création de mises à jour dans les cas. Consultez la documentation de l'API de votre service pour savoir quels sont les champs obligatoires.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4": "Commentaires et mises à jour", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4a": "Créer une mise à jour dans le cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4aDescription": "Définissez les champs pour créer des mises à jour du cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour l’ajout de commentaires aux cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4b": "Ajouter un commentaire dans le cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4bDescription": "Définissez les champs pour ajouter des commentaires au cas dans le système externe. Pour certains systèmes, cela peut être la même méthode que pour la création de mises à jour dans les cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonHelpl": "Objet JSON pour mettre à jour le cas. Utilisez le sélecteur de variable pour ajouter des données de cas à la charge de travail.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonTextFieldLabel": "Objet de mise à jour de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentMethodTextFieldLabel": "Méthode de mise à jour de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlHelp": "URL d’API pour mettre à jour le cas.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlTextFieldLabel": "URL de mise à jour du cas", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.userTextFieldLabel": "Nom d'utilisateur", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.valueTextFieldLabel": "Valeur", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewHeadersSwitch": "Ajouter un en-tête HTTP", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlHelp": "URL pour voir le cas dans le système externe. Utilisez le sélecteur de variable pour ajouter à l’URL l'ID ou le titre du système externe.", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlTextFieldLabel": "URL de visualisation de cas externe", - "xpack.triggersActionsUI.components.builtinActionTypes.common.requiredShortDescTextField": "Une brève description est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientIdHelpLabel": "Configurer l'ID client", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientSecretHelpLabel": "Configurer l'identifiant client secret", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.tenantIdHelpLabel": "Configurer l'ID locataire", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "Envoyer vers la messagerie électronique", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.amazonSesServerTypeLabel": "Amazon SES", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel": "Configurer les comptes de messagerie électronique", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.elasticCloudServerTypeLabel": "Elastic Cloud", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.exchangeServerTypeLabel": "MS Exchange Server", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "Autre", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "Envoyez un e-mail à partir de votre serveur.", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.updateErrorNotificationText": "Impossible d’obtenir la configuration du service", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "L'index d'historique d'alertes doit contenir un suffixe valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidPortText": "Le port n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "L'ID client est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "Le document est requis et doit être un objet JSON valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "Aucune entrée À, Cc ou Cci. Au moins une entrée est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "L'expéditeur est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "L'hôte est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "Le message est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "Le port est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "Le message est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "Le service est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "Le message est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "Le sujet est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "L'ID locataire est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "Le corps est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookSummaryText": "Le titre est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "Données d'index", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "Choisir…", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.configureIndexHelpLabel": "Configuration du connecteur d'index.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.connectorSectionTitle": "Écrire dans l'index", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "Définissez ce champ temporel sur l'heure à laquelle le document a été indexé.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "Définissez l'heure pour chaque document", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "Document à indexer", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.notValidIndexText": "L’index n’est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "Champ temporel", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "Utilisez le caractère * pour élargir votre recherche.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "Exemple de document d'index.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "Basé sur vos vues de données", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesToQueryLabel": "Index", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel": "Éditeur de code", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndex": "Index Elasticsearch", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexDocLink": "Affichez les documents.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshLabel": "Actualiser l'index", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshTooltip": "Actualisez les partitions affectées pour rendre cette opération visible pour la recherche.", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText": "Indexez les données dans Elasticsearch.", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "Token d'API", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "Commentaires supplémentaires", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "Description", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "Adresse e-mail", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "Étiquettes", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "Les étiquettes ne peuvent pas contenir d'espaces.", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "Problème parent", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "Clé de projet", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "Le résumé est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "Taper pour rechercher", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "Taper pour rechercher", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "Chargement...", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.selectMessageText": "Créez un incident dans Jira.", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.severitySelectFieldLabel": "Priorité", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.summaryFieldLabel": "Résumé (requis)", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetFieldsMessage": "Impossible d'obtenir les champs", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "Impossible d'obtenir les problèmes", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "Impossible d'obtenir les types d'erreurs", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "Type d'erreur", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "Envoyer à PagerDuty", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlInvalid": "URL d’API non valide", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "URL de l'API (facultative)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "Classe (facultative)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.componentTextFieldLabel": "Composant (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextFieldLabel": "DedupKey (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextRequiredFieldLabel": "DedupKey", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredDedupKeyText": "DedupKey est requis lors de la résolution ou de la reconnaissance d'un incident.", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredRoutingKeyText": "Une clé d'intégration / clé de routage est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredSummaryText": "Le résumé est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventActionSelectFieldLabel": "Action de l'événement", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectAcknowledgeOptionLabel": "Reconnaissance", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "Résoudre", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "Déclencher", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "Regrouper (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "Configurer un compte PagerDuty", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "Clé d'intégration", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "Envoyez un événement dans PagerDuty.", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectCriticalOptionLabel": "Critique", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectErrorOptionLabel": "Erreur", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectFieldLabel": "Sévérité (facultative)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectInfoOptionLabel": "Info", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectWarningOptionLabel": "Avertissement", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "Source (facultative)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "Résumé", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "Horodatage (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Résilient", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "ID de clé d'API", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "Secret de clé d'API", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "Commentaires supplémentaires", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "Description", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "Nom (requis)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "ID d'organisation", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "Un nom est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "Créez un incident dans IBM Resilient.", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "Sévérité", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "Impossible d'obtenir les types d'incidents", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetSeverityMessage": "Impossible d'obtenir la sévérité", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.urgencySelectFieldLabel": "Type d'incident", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle": "Envoyer vers le log de serveur", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logLevelFieldLabel": "Niveau", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "Message", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "Ajouter un message au log Kibana.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "URL d'instance ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "Application Elastic ServiceNow non installée", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.content": "Veuillez vous rendre dans l'app store ServiceNow pour installer l'application", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.errorMessage": "Message d'erreur", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.cancelButtonText": "Annuler", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.categoryTitle": "Catégorie", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientIdTextFieldLabel": "ID client", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientSecretTextFieldLabel": "Identifiant client secret", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.commentsTextAreaFieldLabel": "Commentaires supplémentaires", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.confirmButtonText": "Mettre à jour", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationDisplay": "Affichage de la corrélation (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationID": "ID corrélation (facultatif)", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutCreate": "ou créez-en un nouveau.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate": "Supprimez ce connecteur", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutTitle": "Ce type de connecteur est déclassé", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "Description", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "Instance source", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.fetchErrorMsg": "Impossible de récupérer. Vérifiez l'URL ou la configuration CORS de votre instance ServiceNow.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "Impact", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "Pour utiliser ce connecteur, installez d'abord l'application Elastic à partir de l'app store ServiceNow.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "L'URL n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.keyIdTextFieldLabel": "ID de clé du vérificateur JWT", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "Clé de message", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "Nom de l'indicateur", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "Nœud", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "Mot de passe", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "Priorité", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassLabelHelpText": "Il est requis uniquement si vous avez défini un mot de passe sur votre clé privée", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassTextFieldLabel": "Mot de passe de clé privée", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyTextFieldLabel": "Clé privée", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredClientIdTextField": "L'ID client est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField": "L'ID de clé du vérificateur JWT est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPrivateKeyTextField": "La clé privée est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "La sévérité est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserIdentifierTextField": "L'identifiant de l'utilisateur est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "Le nom d'utilisateur est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "Ressource", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "configurer une instance de développeur", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "Sévérité (requise)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severitySelectFieldLabel": "Sévérité", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.snInstanceLabel": "Instance ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.sourceTextAreaFieldLabel": "Source", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.subcategoryTitle": "Sous-catégorie", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.title": "Incident", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.titleFieldLabel": "Brève description (requise)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.typeTextAreaFieldLabel": "Type", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unableToGetChoicesMessage": "Impossible d'obtenir les choix", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unknown": "INCONNU", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateCalloutText": "Le connecteur a été mis à jour.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormCredentialsTitle": "Fournir les informations d'authentification", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormInstallTitle": "Installer l'application Elastic ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle": "Mettre à jour le connecteur ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormUrlTitle": "Entrer votre URL d'instance ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.urgencySelectFieldLabel": "Urgence", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.useOAuth": "Utiliser l'authentification OAuth", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.userEmailTextFieldLabel": "Identifiant de l'utilisateur", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.usernameTextFieldLabel": "Nom d'utilisateur", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.visitSNStore": "Visiter l'app store ServiceNow", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.warningMessage": "Cette action mettra à jour toutes les instances de ce connecteur et ne pourra pas être annulée.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.correlationIDHelpLabel": "Identificateur pour les incidents de mise à jour", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.actionTypeTitle": "ServiceNow ITOM", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowITOM.event": "Événement", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.selectMessageText": "Créez un événement dans ServiceNow ITOM.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.actionTypeTitle": "ServiceNow ITSM", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.selectMessageText": "Créez un incident dans ServiceNow ITSM.", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.actionTypeTitle": "ServiceNow SecOps", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.selectMessageText": "Créez un incident dans ServiceNow SecOps.", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowSIR.title": "Incident de sécurité", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "Identificateur pour les incidents de mise à jour", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "Envoyer vers Slack", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "Message", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "Envoyez un message à un canal ou à un utilisateur Slack.", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "Créer une URL de webhook Slack", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "URL de webhook", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationFieldsMessage": "Impossible d'obtenir les champs de l'application", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "Créer l'enregistrement Swimlane", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "ID de l'alerte", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "Fournir un token d'API Swimlane", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "Token d'API", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "URL d'API", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.appIdTextFieldLabel": "ID d'application", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseIdFieldLabel": "ID de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseNameFieldLabel": "Nom de cas", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.commentsFieldLabel": "Commentaires", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.configureConnectionLabel": "Configurer la connexion de l'API", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.connectorType": "Type de connecteur", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.descriptionFieldLabel": "Description", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "Ce connecteur ne peut pas être sélectionné, car il ne possède pas les mappings de champs d'alerte requis. Vous pouvez modifier ce connecteur pour ajouter les mappings de champs requis ou sélectionner un connecteur de type Alertes.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "Ce connecteur ne possède pas de mappings de champs", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "L'ID d'alerte est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "Un ID d'application est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "L'ID de cas est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "Le nom de cas est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "Les commentaires sont requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "La description est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "Le nom de règle est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "La sévérité est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "L'URL n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "Configurer les mappings de champs", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "Suivant", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "Si les mappings de champs ne sont pas configurés, le type de connecteur Swimlane sera défini sur Tous.", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "Retour", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "Nom de règle", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "Créer un enregistrement dans Swimlane", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "Sévérité", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "Envoyer un message à un canal Microsoft Teams.", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "Le message est requis.", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.webhookUrlTextLabel": "URL de webhook", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "Message", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "Envoyer un message à un canal Microsoft Teams.", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "Créer une URL de webhook Microsoft Teams", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Données de webhook", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButtonLabel": "Ajouter un en-tête", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "Éditeur de code", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "Corps", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "L'URL n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "Demander une authentification pour ce webhook", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerKeyTextFieldLabel": "Clé", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerValueTextFieldLabel": "Valeur", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "Méthode", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "Mot de passe", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.removeHeaderIconLabel": "Clé", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "Envoyer une requête à un service Web.", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "Nom d'utilisateur", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "Ajouter un en-tête HTTP", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "Données xMatters", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthButtonGroupLegend": "Authentification de base", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "Authentification de base", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "Sélectionnez la méthode d'authentification utilisée pour la configuration du déclencheur xMatters.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "L'URL n'est pas valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUsernameTextField": "Nom d'utilisateur non valide.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "L'URL est requise.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "Spécifiez l'URL xMatters complète.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "Mot de passe", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "Déclenchez un workflow xMatters.", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "Sévérité", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "Critique", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectHighOptionLabel": "Élevé", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectLowOptionLabel": "Bas", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMediumOptionLabel": "Moyenne", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMinimalOptionLabel": "Minimale", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.tags": "Balises", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlAuthLabel": "Authentification de l'URL", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlLabel": "URL d'initiation", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userCredsLabel": "Identifiants d'utilisateur", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userTextFieldLabel": "Nom d'utilisateur", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorButtonLabel": "Créer un connecteur", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyBody": "Configurer les services de messagerie électronique, Slack, Elasticsearch et tiers que Kibana exécute.", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyTitle": "Créer votre premier connecteur", @@ -32353,8 +32380,6 @@ "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorMessage": "L'éditeur est introuvable. Veuillez actualiser la page et réessayer", "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorTitle": "Impossible d'ajouter une variable de message", "xpack.triggersActionsUI.components.simpleConnectorForm.secrets.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningDesc": "Configurez les champs Create Comment URL et Create Comment Objects pour que le connecteur puisse partager les commentaires.", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningTitle": "Impossible de partager les commentaires du cas", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "Connecteurs", "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart] : est postérieure à [dateEnd]", "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval] : doit être spécifié si [dateStart] n'est pas égale à [dateEnd]", @@ -32427,7 +32452,6 @@ "xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "Chargement des paramètres du connecteur…", "xpack.triggersActionsUI.sections.actionForm.actionSectionsTitle": "Actions", "xpack.triggersActionsUI.sections.actionForm.addActionButtonLabel": "Ajouter une action", - "xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle": "Connecteur déclassé", "xpack.triggersActionsUI.sections.actionForm.getMoreConnectorsTitle": "Obtenir davantage de connecteurs", "xpack.triggersActionsUI.sections.actionForm.getMoreRuleTypesTitle": "Obtenir davantage de types de règles", "xpack.triggersActionsUI.sections.actionForm.incidentManagementSystemLabel": "Système de gestion des incidents", @@ -32466,17 +32490,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.actionErrorToolTip": "L’action contient des erreurs.", "xpack.triggersActionsUI.sections.actionTypeForm.actionRunWhenInActionGroup": "Exécuter quand", "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "Ajouter un connecteur", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateCommentMethodText": "La méthode de création de commentaire est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateIncidentResponseKeyText": "La clé d’ID de cas pour la réponse de création de cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateMethodText": "La méthode de création de cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseCreatedKeyText": "La clé de date de création de la réponse d’obtention de cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseExternalTitleKeyText": "La clé de titre du cas externe pour la réponse d’obtention de cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseUpdatedKeyText": "La clé de date de mise à jour de la réponse d’obtention de cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentViewUrlKeyText": "L'URL de visualisation du cas est requise.", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredUpdateMethodText": "La méthode de mise à jour du cas est requise.", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "La méthode est requise.", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "Sélectionner un connecteur", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "Création de \"{connectorName}\" effectuée", "xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "Annuler", @@ -32486,25 +32499,6 @@ "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "Raison", "xpack.triggersActionsUI.sections.alertsTable.column.actions": "Actions", "xpack.triggersActionsUI.sections.alertsTable.leadingControl.viewDetails": "Afficher les détails", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "Cci", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "Cc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "Authentification", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientIdFieldLabel": "ID client", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientSecretTextFieldLabel": "Identifiant client secret", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel": "Expéditeur", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel": "Demander une authentification pour ce serveur", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel": "Hôte", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.messageTextAreaFieldLabel": "Message", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel": "Mot de passe", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel": "Port", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel": "Cci", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel": "Cc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel": "À", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.secureSwitchLabel": "Sécurisé", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.serviceTextFieldLabel": "Service", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel": "Objet", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.tenantIdFieldLabel": "ID locataire", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel": "Nom d'utilisateur", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseCancelButtonText": "Annuler", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseConfirmButtonText": "Abandonner les modifications", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseMessage": "Vous ne pouvez pas récupérer de modifications non enregistrées.", @@ -32694,7 +32688,6 @@ "xpack.triggersActionsUI.sections.rulesList.removeAllButton": "Tout supprimer", "xpack.triggersActionsUI.sections.rulesList.removeCancelButton": "Annuler", "xpack.triggersActionsUI.sections.rulesList.removeConfirmButton": "Tout supprimer", - "xpack.triggersActionsUI.sections.rulesList.resetDefaultIndexLabel": "Réinitialiser l'index par défaut", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDecrypting": "Une erreur s'est produite lors du déchiffrement de la règle.", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDisabled": "La règle n'a pas pu s'exécuter, car elle a été lancée après sa désactivation.", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonLicense": "Impossible d'exécuter la règle", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 672dcbd3d5cc..a09bdfe578cb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6371,9 +6371,383 @@ "xpack.stackConnectors.xmatters.shouldNotHaveSecretsUrl": "usesBasicがtrueのときには、secretsUrlを指定しないでください", "xpack.stackConnectors.xmatters.shouldNotHaveUsernamePassword": "usesBasicがfalseのときには、ユーザー名とパスワードを指定しないでください", "xpack.stackConnectors.xmatters.title": "xMatters", + "xpack.stackConnectors.components.index.error.badIndexOverrideValue": "アラート履歴インデックスの先頭は\"{alertHistoryPrefix}\"でなければなりません。", + "xpack.stackConnectors.components.email.error.invalidEmail": "電子メールアドレス{email}が無効です。", + "xpack.stackConnectors.components.email.error.notAllowed": "電子メールアドレス{email}が許可されていません。", + "xpack.stackConnectors.components.index.preconfiguredIndexHelpText": "ドキュメントは{alertHistoryIndex}インデックスにインデックスされます。", + "xpack.stackConnectors.components.jira.unableToGetIssueMessage": "ID {id}の問題を取得できません", + "xpack.stackConnectors.components.pagerDuty.error.invalidTimestamp": "タイムスタンプは、{nowShortFormat}や{nowLongFormat}などの有効な日付でなければなりません。", + "xpack.stackConnectors.components.serviceNow.apiInfoError": "アプリケーション情報の取得を試みるときの受信ステータス:{status}", + "xpack.stackConnectors.components.serviceNow.appInstallationInfo": "{update} {create} ", + "xpack.stackConnectors.components.serviceNow.updateSuccessToastTitle": "{connectorName}コネクターが更新されました", + "xpack.stackConnectors.components.serviceNow.apiUrlHelpLabel": "任意のServiceNowインスタンスへの完全なURLを入力します。ない場合は、{instance}。", + "xpack.stackConnectors.components.serviceNow.appRunning": "更新を実行する前に、ServiceNowアプリストアからElasticアプリをインストールする必要があります。アプリをインストールするには、{visitLink}", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationMessage": "id {id}のアプリケーションフィールドを取得できません", + "xpack.stackConnectors.components.casesWebhook.commentsTextAreaFieldLabel": "追加のコメント", + "xpack.stackConnectors.components.casesWebhook.descriptionTextAreaFieldLabel": "説明", + "xpack.stackConnectors.components.casesWebhook.tagsFieldLabel": "タグ", + "xpack.stackConnectors.components.casesWebhook.titleFieldLabel": "概要(必須)", + "xpack.stackConnectors.components.casesWebhook.addHeaderButton": "追加", + "xpack.stackConnectors.components.casesWebhook.addVariable": "変数を追加", + "xpack.stackConnectors.components.casesWebhook.authenticationLabel": "認証", + "xpack.stackConnectors.components.casesWebhook.caseCommentDesc": "Kibanaケースコメント", + "xpack.stackConnectors.components.casesWebhook.caseDescriptionDesc": "Kibanaケース説明", + "xpack.stackConnectors.components.casesWebhook.caseTagsDesc": "Kibanaケースタグ", + "xpack.stackConnectors.components.casesWebhook.caseTitleDesc": "Kibanaケースタイトル", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonHelp": "コメントを作成するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonTextFieldLabel": "コメントオブジェクトを作成", + "xpack.stackConnectors.components.casesWebhook.createCommentMethodTextFieldLabel": "コメントメソッドを作成", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlHelp": "コメントをケースに追加するためのAPI URL。", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlTextFieldLabel": "コメントURLを作成", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonHelpText": "ケースを作成するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonTextFieldLabel": "ケースオブジェクトを作成", + "xpack.stackConnectors.components.casesWebhook.createIncidentMethodTextFieldLabel": "ケースメソッドを作成", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyHelpText": "外部ケースIDを含むケース対応の作成のJSONキー", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyTextFieldLabel": "ケース対応ケースキーを作成", + "xpack.stackConnectors.components.casesWebhook.createIncidentUrlTextFieldLabel": "ケースURLを作成", + "xpack.stackConnectors.components.casesWebhook.deleteHeaderButton": "削除", + "xpack.stackConnectors.components.casesWebhook.docLink": "Webフックの構成 - ケース管理コネクター。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentIncidentText": "コメントオブジェクトの作成は有効なJSONでなければなりません。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentUrlText": "コメントURLの作成はURL形式でなければなりません。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentText": "ケースオブジェクトの作成は必須であり、有効なJSONでなければなりません。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateUrlText": "ケースURLの作成は必須です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentUrlText": "ケースURLの取得は必須です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateIncidentText": "ケースオブジェクトの更新は必須であり、有効なJSONでなければなりません。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateUrlText": "ケースURLの更新は必須です。", + "xpack.stackConnectors.components.casesWebhook.externalIdDesc": "外部システムID", + "xpack.stackConnectors.components.casesWebhook.externalTitleDesc": "外部システムタイトル", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyHelp": "外部ケースタイトルを含むケース対応の取得のJSONキー", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyTextFieldLabel": "ケース対応の取得の外部タイトルキー", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlHelp": "外部システムからケース詳細JSONを取得するAPI URL。変数セレクターを使用して、外部システムIDをURLに追加します。", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlTextFieldLabel": "ケースURLを取得", + "xpack.stackConnectors.components.casesWebhook.hasAuthSwitchLabel": "この Web フックの認証が必要です", + "xpack.stackConnectors.components.casesWebhook.httpHeadersTitle": "使用中のヘッダー", + "xpack.stackConnectors.components.casesWebhook.jsonCodeEditorAriaLabel": "コードエディター", + "xpack.stackConnectors.components.casesWebhook.jsonFieldLabel": "JSON", + "xpack.stackConnectors.components.casesWebhook.keyTextFieldLabel": "キー", + "xpack.stackConnectors.components.casesWebhook.next": "次へ", + "xpack.stackConnectors.components.casesWebhook.passwordTextFieldLabel": "パスワード", + "xpack.stackConnectors.components.casesWebhook.previous": "前へ", + "xpack.stackConnectors.components.casesWebhook.selectMessageText": "ケース管理Webサービスにリクエストを送信します。", + "xpack.stackConnectors.components.casesWebhook.step1": "コネクターを設定", + "xpack.stackConnectors.components.casesWebhook.step2": "ケースを作成", + "xpack.stackConnectors.components.casesWebhook.step2Description": "外部システムでケースを作成するフィールドを設定します。必要なフィールドを判断するには、サービスのAPIドキュメントを確認してください", + "xpack.stackConnectors.components.casesWebhook.step3": "ケース情報を取得", + "xpack.stackConnectors.components.casesWebhook.step3Description": "外部システムでコメントをケースに追加するフィールドを設定します。一部のシステムでは、ケースでの更新の作成と同じメソッドの場合があります。必要なフィールドを判断するには、サービスのAPIドキュメントを確認してください。", + "xpack.stackConnectors.components.casesWebhook.step4": "コメントと更新", + "xpack.stackConnectors.components.casesWebhook.step4a": "ケースで更新を作成", + "xpack.stackConnectors.components.casesWebhook.step4aDescription": "外部システムでケースの更新を作成するフィールドを設定します。一部のシステムでは、ケースへのコメントの追加と同じメソッドの場合があります。", + "xpack.stackConnectors.components.casesWebhook.step4b": "ケースでコメントを追加", + "xpack.stackConnectors.components.casesWebhook.step4bDescription": "外部システムでコメントをケースに追加するフィールドを設定します。一部のシステムでは、ケースでの更新の作成と同じメソッドの場合があります。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonHelpl": "ケースを更新するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonTextFieldLabel": "ケースオブジェクトを更新", + "xpack.stackConnectors.components.casesWebhook.updateIncidentMethodTextFieldLabel": "ケースメソッドを更新", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlHelp": "ケースを更新するAPI URL。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlTextFieldLabel": "ケースURLを更新", + "xpack.stackConnectors.components.casesWebhook.userTextFieldLabel": "ユーザー名", + "xpack.stackConnectors.components.casesWebhook.valueTextFieldLabel": "値", + "xpack.stackConnectors.components.casesWebhook.viewHeadersSwitch": "HTTP ヘッダーを追加", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlHelp": "外部システムでケースを表示するURL。変数セレクターを使用して、外部システムIDまたは外部システムタイトルをURLに追加します。", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlTextFieldLabel": "外部ケース表示URL", + "xpack.stackConnectors.components.serviceNow.requiredShortDescTextField": "短い説明が必要です。", + "xpack.stackConnectors.components.email.exchangeForm.clientIdHelpLabel": "クライアントIDの構成", + "xpack.stackConnectors.components.email.exchangeForm.clientSecretHelpLabel": "クライアントシークレットの構成", + "xpack.stackConnectors.components.email.exchangeForm.tenantIdHelpLabel": "テナントIDの構成", + "xpack.stackConnectors.components.email.connectorTypeTitle": "メールに送信", + "xpack.stackConnectors.components.email.amazonSesServerTypeLabel": "Amazon SES", + "xpack.stackConnectors.components.email.configureAccountsHelpLabel": "電子メールアカウントの構成", + "xpack.stackConnectors.components.email.elasticCloudServerTypeLabel": "Elastic Cloud", + "xpack.stackConnectors.components.email.exchangeServerTypeLabel": "MS Exchange Server", + "xpack.stackConnectors.components.email.gmailServerTypeLabel": "Gmail", + "xpack.stackConnectors.components.email.otherServerTypeLabel": "その他", + "xpack.stackConnectors.components.email.outlookServerTypeLabel": "Outlook", + "xpack.stackConnectors.components.email.selectMessageText": "サーバーからメールを送信します。", + "xpack.stackConnectors.components.email.updateErrorNotificationText": "サービス構成を取得できません", + "xpack.stackConnectors.components.index.error.badIndexOverrideSuffix": "アラート履歴インデックスには有効なサフィックスを含める必要があります。", + "xpack.stackConnectors.components.email.error.invalidPortText": "ポートが無効です。", + "xpack.stackConnectors.components.email.error.requiredAuthUserNameText": "ユーザー名が必要です。", + "xpack.stackConnectors.components.email.error.requiredClientIdText": "クライアントIDは必須です。", + "xpack.stackConnectors.components.index.error.requiredDocumentJson": "ドキュメントが必要です。有効なJSONオブジェクトにしてください。", + "xpack.stackConnectors.components.email.error.requiredEntryText": "To、Cc、または Bcc のエントリーがありません。 1 つ以上のエントリーが必要です。", + "xpack.stackConnectors.components.email.error.requiredFromText": "送信元が必要です。", + "xpack.stackConnectors.components.email.error.requiredHostText": "ホストが必要です。", + "xpack.stackConnectors.components.email.error.requiredMessageText": "メッセージが必要です。", + "xpack.stackConnectors.components.email.error.requiredPortText": "ポートが必要です。", + "xpack.stackConnectors.components.serverLog.error.requiredServerLogMessageText": "メッセージが必要です。", + "xpack.stackConnectors.components.email.error.requiredServiceText": "サービスは必須です。", + "xpack.stackConnectors.components.email.error.requiredSubjectText": "件名が必要です。", + "xpack.stackConnectors.components.email.error.requiredTenantIdText": "テナントIDは必須です。", + "xpack.stackConnectors.components.webhook.error.requiredWebhookBodyText": "本文が必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredWebhookSummaryText": "タイトルが必要です。", + "xpack.stackConnectors.components.index.connectorTypeTitle": "データをインデックスする", + "xpack.stackConnectors.components.index.configureIndexHelpLabel": "インデックスコネクターを構成しています。", + "xpack.stackConnectors.components.index.connectorSectionTitle": "インデックスを書き出す", + "xpack.stackConnectors.components.index.definedateFieldTooltip": "この時間フィールドをドキュメントにインデックスが作成された時刻に設定します。", + "xpack.stackConnectors.components.index.defineTimeFieldLabel": "各ドキュメントの時刻フィールドを定義", + "xpack.stackConnectors.components.index.documentsFieldLabel": "インデックスするドキュメント", + "xpack.stackConnectors.components.index.error.notValidIndexText": "インデックスは有効ではありません。", + "xpack.stackConnectors.components.index.executionTimeFieldLabel": "時間フィールド", + "xpack.stackConnectors.components.index.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。", + "xpack.stackConnectors.components.index.indexDocumentHelpLabel": "インデックスドキュメントの例。", + "xpack.stackConnectors.components.index.indicesToQueryLabel": "インデックス", + "xpack.stackConnectors.components.index.jsonDocAriaLabel": "コードエディター", + "xpack.stackConnectors.components.index.preconfiguredIndex": "Elasticsearchインデックス", + "xpack.stackConnectors.components.index.preconfiguredIndexDocLink": "ドキュメントを表示します。", + "xpack.stackConnectors.components.index.refreshLabel": "更新インデックス", + "xpack.stackConnectors.components.index.refreshTooltip": "影響を受けるシャードを更新し、この処理を検索できるようにします。", + "xpack.stackConnectors.components.index.selectMessageText": "データを Elasticsearch にインデックスしてください。", + "xpack.stackConnectors.components.jira.connectorTypeTitle": "Jira", + "xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "APIトークン", + "xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.jira.commentsTextAreaFieldLabel": "追加のコメント", + "xpack.stackConnectors.components.jira.descriptionTextAreaFieldLabel": "説明", + "xpack.stackConnectors.components.jira.emailTextFieldLabel": "メールアドレス", + "xpack.stackConnectors.components.jira.impactSelectFieldLabel": "ラベル", + "xpack.stackConnectors.components.jira.labelsSpacesErrorMessage": "ラベルにはスペースを使用できません。", + "xpack.stackConnectors.components.jira.parentIssueSearchLabel": "親問題", + "xpack.stackConnectors.components.jira.projectKey": "プロジェクトキー", + "xpack.stackConnectors.components.jira.requiredSummaryTextField": "概要が必要です。", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxAriaLabel": "入力して検索", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxPlaceholder": "入力して検索", + "xpack.stackConnectors.components.jira.searchIssuesLoading": "読み込み中...", + "xpack.stackConnectors.components.jira.selectMessageText": "Jira でインシデントを作成します。", + "xpack.stackConnectors.components.jira.severitySelectFieldLabel": "優先度", + "xpack.stackConnectors.components.jira.summaryFieldLabel": "概要(必須)", + "xpack.stackConnectors.components.jira.unableToGetFieldsMessage": "フィールドを取得できません", + "xpack.stackConnectors.components.jira.unableToGetIssuesMessage": "問題を取得できません", + "xpack.stackConnectors.components.jira.unableToGetIssueTypesMessage": "問題タイプを取得できません", + "xpack.stackConnectors.components.jira.urgencySelectFieldLabel": "問題タイプ", + "xpack.stackConnectors.components.pagerDuty.connectorTypeTitle": "PagerDuty に送信", + "xpack.stackConnectors.components.pagerDuty.apiUrlInvalid": "無効なAPI URL", + "xpack.stackConnectors.components.pagerDuty.apiUrlTextFieldLabel": "API URL(任意)", + "xpack.stackConnectors.components.pagerDuty.classFieldLabel": "クラス(任意)", + "xpack.stackConnectors.components.pagerDuty.componentTextFieldLabel": "コンポーネント(任意)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextFieldLabel": "DedupKey(任意)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextRequiredFieldLabel": "DedupKey", + "xpack.stackConnectors.components.pagerDuty.error.requiredDedupKeyText": "インシデントを解決または確認するときには、DedupKeyが必要です。", + "xpack.stackConnectors.components.pagerDuty.error.requiredRoutingKeyText": "統合キー/ルーティングキーが必要です。", + "xpack.stackConnectors.components.pagerDuty.error.requiredSummaryText": "概要が必要です。", + "xpack.stackConnectors.components.pagerDuty.eventActionSelectFieldLabel": "イベントアクション", + "xpack.stackConnectors.components.pagerDuty.eventSelectAcknowledgeOptionLabel": "承認", + "xpack.stackConnectors.components.pagerDuty.eventSelectResolveOptionLabel": "解決", + "xpack.stackConnectors.components.pagerDuty.eventSelectTriggerOptionLabel": "トリガー", + "xpack.stackConnectors.components.pagerDuty.groupTextFieldLabel": "グループ(任意)", + "xpack.stackConnectors.components.pagerDuty.routingKeyNameHelpLabel": "PagerDuty アカウントを構成します", + "xpack.stackConnectors.components.pagerDuty.routingKeyTextFieldLabel": "統合キー", + "xpack.stackConnectors.components.pagerDuty.selectMessageText": "PagerDuty でイベントを送信します。", + "xpack.stackConnectors.components.pagerDuty.severitySelectCriticalOptionLabel": "重大", + "xpack.stackConnectors.components.pagerDuty.severitySelectErrorOptionLabel": "エラー", + "xpack.stackConnectors.components.pagerDuty.severitySelectFieldLabel": "重要度(任意)", + "xpack.stackConnectors.components.pagerDuty.severitySelectInfoOptionLabel": "情報", + "xpack.stackConnectors.components.pagerDuty.severitySelectWarningOptionLabel": "警告", + "xpack.stackConnectors.components.pagerDuty.sourceTextFieldLabel": "ソース(任意)", + "xpack.stackConnectors.components.pagerDuty.summaryFieldLabel": "まとめ", + "xpack.stackConnectors.components.pagerDuty.timestampTextFieldLabel": "タイムスタンプ(任意)", + "xpack.stackConnectors.components.resilient.connectorTypeTitle": "Resilient", + "xpack.stackConnectors.components.resilient.apiKeyId": "APIキーID", + "xpack.stackConnectors.components.resilient.apiKeySecret": "APIキーシークレット", + "xpack.stackConnectors.components.resilient.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.resilient.commentsTextAreaFieldLabel": "追加のコメント", + "xpack.stackConnectors.components.resilient.descriptionTextAreaFieldLabel": "説明", + "xpack.stackConnectors.components.resilient.nameFieldLabel": "名前(必須)", + "xpack.stackConnectors.components.resilient.orgId": "組織 ID", + "xpack.stackConnectors.components.resilient.requiredNameTextField": "名前が必要です。", + "xpack.stackConnectors.components.resilient.selectMessageText": "IBM Resilient でインシデントを作成します。", + "xpack.stackConnectors.components.resilient.severity": "深刻度", + "xpack.stackConnectors.components.resilient.unableToGetIncidentTypesMessage": "インシデントタイプを取得できません", + "xpack.stackConnectors.components.resilient.unableToGetSeverityMessage": "深刻度を取得できません", + "xpack.stackConnectors.components.resilient.urgencySelectFieldLabel": "インシデントタイプ", + "xpack.stackConnectors.components.serverLog.connectorTypeTitle": "サーバーログに送信", + "xpack.stackConnectors.components.serverLog.logLevelFieldLabel": "レベル", + "xpack.stackConnectors.components.serverLog.logMessageFieldLabel": "メッセージ", + "xpack.stackConnectors.components.serverLog.selectMessageText": "Kibana ログにメッセージを追加します。", + "xpack.stackConnectors.components.serviceNow.apiUrlTextFieldLabel": "ServiceNowインスタンスURL", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout": "Elastic ServiceNowアプリがインストールされていません", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.content": "ServiceNowアプリストアに移動し、アプリケーションをインストールしてください", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.errorMessage": "エラーメッセージ", + "xpack.stackConnectors.components.serviceNow.authenticationLabel": "認証", + "xpack.stackConnectors.components.serviceNow.cancelButtonText": "キャンセル", + "xpack.stackConnectors.components.serviceNow.categoryTitle": "カテゴリー", + "xpack.stackConnectors.components.serviceNow.clientIdTextFieldLabel": "クライアントID", + "xpack.stackConnectors.components.serviceNow.clientSecretTextFieldLabel": "クライアントシークレット", + "xpack.stackConnectors.components.serviceNow.commentsTextAreaFieldLabel": "追加のコメント", + "xpack.stackConnectors.components.serviceNow.confirmButtonText": "更新", + "xpack.stackConnectors.components.serviceNow.correlationDisplay": "相関関係表示(オプション)", + "xpack.stackConnectors.components.serviceNow.correlationID": "相関関係ID(オプション)", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutCreate": "または新規作成します。", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutMigrate": "このコネクターを更新します", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutTitle": "このコネクターは廃止予定です", + "xpack.stackConnectors.components.serviceNow.descriptionTextAreaFieldLabel": "説明", + "xpack.stackConnectors.components.serviceNow.eventClassTextAreaFieldLabel": "ソースインスタンス", + "xpack.stackConnectors.components.serviceNow.fetchErrorMsg": "取得できませんでした。ServiceNowインスタンスのURLまたはCORS公正を確認します。", + "xpack.stackConnectors.components.serviceNow.impactSelectFieldLabel": "インパクト", + "xpack.stackConnectors.components.serviceNow.installationCalloutTitle": "このコネクターを使用するには、まずServiceNowアプリストアからElasticアプリをインストールします。", + "xpack.stackConnectors.components.serviceNow.invalidApiUrlTextField": "URL が無効です。", + "xpack.stackConnectors.components.serviceNow.keyIdTextFieldLabel": "JWT VerifierキーID", + "xpack.stackConnectors.components.serviceNow.messageKeyTextAreaFieldLabel": "メッセージキー", + "xpack.stackConnectors.components.serviceNow.metricNameTextAreaFieldLabel": "メトリック名", + "xpack.stackConnectors.components.serviceNow.nodeTextAreaFieldLabel": "ノード", + "xpack.stackConnectors.components.serviceNow.passwordTextFieldLabel": "パスワード", + "xpack.stackConnectors.components.serviceNow.prioritySelectFieldLabel": "優先度", + "xpack.stackConnectors.components.serviceNow.privateKeyPassLabelHelpText": "これは、秘密鍵でパスワードを設定した場合にのみ必要です", + "xpack.stackConnectors.components.serviceNow.privateKeyPassTextFieldLabel": "秘密鍵パスワード", + "xpack.stackConnectors.components.serviceNow.privateKeyTextFieldLabel": "秘密鍵", + "xpack.stackConnectors.components.serviceNow.requiredClientIdTextField": "クライアントIDは必須です。", + "xpack.stackConnectors.components.serviceNow.requiredKeyIdTextField": "JWT VerifierキーIDは必須です。", + "xpack.stackConnectors.components.serviceNow.requiredPrivateKeyTextField": "秘密鍵は必須です。", + "xpack.stackConnectors.components.serviceNow.requiredSeverityTextField": "重要度は必須です。", + "xpack.stackConnectors.components.serviceNow.requiredUserIdentifierTextField": "ユーザーIDは必須です。", + "xpack.stackConnectors.components.serviceNow.requiredUsernameTextField": "ユーザー名が必要です。", + "xpack.stackConnectors.components.serviceNow.resourceTextAreaFieldLabel": "リソース", + "xpack.stackConnectors.components.serviceNow.setupDevInstance": "開発者インスタンスを設定", + "xpack.stackConnectors.components.serviceNow.severityRequiredSelectFieldLabel": "重要度(必須)", + "xpack.stackConnectors.components.serviceNow.severitySelectFieldLabel": "深刻度", + "xpack.stackConnectors.components.serviceNow.snInstanceLabel": "ServiceNowインスタンス", + "xpack.stackConnectors.components.serviceNow.sourceTextAreaFieldLabel": "送信元", + "xpack.stackConnectors.components.serviceNow.subcategoryTitle": "サブカテゴリー", + "xpack.stackConnectors.components.serviceNow.title": "インシデント", + "xpack.stackConnectors.components.serviceNow.titleFieldLabel": "短い説明(必須)", + "xpack.stackConnectors.components.serviceNow.typeTextAreaFieldLabel": "型", + "xpack.stackConnectors.components.serviceNow.unableToGetChoicesMessage": "選択肢を取得できません", + "xpack.stackConnectors.components.serviceNow.unknown": "不明", + "xpack.stackConnectors.components.serviceNow.updateCalloutText": "コネクターが更新されました。", + "xpack.stackConnectors.components.serviceNow.updateFormCredentialsTitle": "認証資格情報を入力", + "xpack.stackConnectors.components.serviceNow.updateFormInstallTitle": "Elastic ServiceNowアプリをインストール", + "xpack.stackConnectors.components.serviceNow.updateFormTitle": "ServiceNowコネクターを更新", + "xpack.stackConnectors.components.serviceNow.updateFormUrlTitle": "ServiceNowインスタンスURLを入力", + "xpack.stackConnectors.components.serviceNow.urgencySelectFieldLabel": "緊急", + "xpack.stackConnectors.components.serviceNow.useOAuth": "OAuth認証を使用", + "xpack.stackConnectors.components.serviceNow.userEmailTextFieldLabel": "ユーザーID", + "xpack.stackConnectors.components.serviceNow.usernameTextFieldLabel": "ユーザー名", + "xpack.stackConnectors.components.serviceNow.visitSNStore": "ServiceNowアプリストアにアクセス", + "xpack.stackConnectors.components.serviceNow.warningMessage": "このコネクターのすべてのインスタンスが更新され、元に戻せません。", + "xpack.stackConnectors.components.serviceNow.correlationIDHelpLabel": "インシデントを更新するID", + "xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle": "ServiceNow ITOM", + "xpack.stackConnectors.components.serviceNowITOM.event": "イベント", + "xpack.stackConnectors.components.serviceNowITOM.selectMessageText": "ServiceNow ITOMでイベントを作成します。", + "xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle": "ServiceNow ITSM", + "xpack.stackConnectors.components.serviceNowITSM.selectMessageText": "ServiceNow ITSMでインシデントを作成します。", + "xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle": "ServiceNow SecOps", + "xpack.stackConnectors.components.serviceNowSIR.selectMessageText": "ServiceNow SecOpsでインシデントを作成します。", + "xpack.stackConnectors.components.serviceNowSIR.title": "セキュリティインシデント", + "xpack.stackConnectors.components.serviceNowSIR.correlationIDHelpLabel": "インシデントを更新するID", + "xpack.stackConnectors.components.slack.connectorTypeTitle": "Slack に送信", + "xpack.stackConnectors.components.slack.error.invalidWebhookUrlText": "Web フック URL が無効です。", + "xpack.stackConnectors.components.slack.messageTextAreaFieldLabel": "メッセージ", + "xpack.stackConnectors.components.slack.selectMessageText": "Slack チャネルにメッセージを送信します。", + "xpack.stackConnectors.components.slack.webhookUrlHelpLabel": "Slack Web フック URL を作成", + "xpack.stackConnectors.components.slack.webhookUrlTextFieldLabel": "Web フック URL", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationFieldsMessage": "アプリケーションフィールドを取得できません", + "xpack.stackConnectors.components.swimlane.connectorTypeTitle": "Swimlaneレコードを作成", + "xpack.stackConnectors.components.swimlane.alertIdFieldLabel": "アラートID", + "xpack.stackConnectors.components.swimlane.apiTokenNameHelpLabel": "Swimlane APIトークンを指定", + "xpack.stackConnectors.components.swimlane.apiTokenTextFieldLabel": "APIトークン", + "xpack.stackConnectors.components.swimlane.apiUrlTextFieldLabel": "API Url", + "xpack.stackConnectors.components.swimlane.appIdTextFieldLabel": "アプリケーションID", + "xpack.stackConnectors.components.swimlane.caseIdFieldLabel": "ケースID", + "xpack.stackConnectors.components.swimlane.caseNameFieldLabel": "ケース名", + "xpack.stackConnectors.components.swimlane.commentsFieldLabel": "コメント", + "xpack.stackConnectors.components.swimlane.configureConnectionLabel": "API接続を構成", + "xpack.stackConnectors.components.swimlane.connectorType": "コネクタータイプ", + "xpack.stackConnectors.components.swimlane.descriptionFieldLabel": "説明", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningDesc": "このコネクターを選択できません。必要なアラートフィールドマッピングがありません。このコネクターを編集して、必要なフィールドマッピングを追加するか、タイプがアラートのコネクターを選択できます。", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningTitle": "このコネクターにはフィールドマッピングがありません。", + "xpack.stackConnectors.components.swimlane.error.requiredAlertID": "アラートIDは必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredAppIdText": "アプリIDは必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredCaseID": "ケースIDは必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredCaseName": "ケース名は必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredComments": "コメントは必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredDescription": "説明が必要です。", + "xpack.stackConnectors.components.swimlane.error.requiredRuleName": "ルール名は必須です。", + "xpack.stackConnectors.components.swimlane.error.requiredSeverity": "重要度は必須です。", + "xpack.stackConnectors.components.swimlane.invalidApiUrlTextField": "URL が無効です。", + "xpack.stackConnectors.components.swimlane.mappingTitleTextFieldLabel": "フィールドマッピングを構成", + "xpack.stackConnectors.components.swimlane.nextStep": "次へ", + "xpack.stackConnectors.components.swimlane.nextStepHelpText": "フィールドマッピングが構成されていない場合、Swimlaneコネクタータイプはすべてに設定されます。", + "xpack.stackConnectors.components.swimlane.prevStep": "戻る", + "xpack.stackConnectors.components.swimlane.ruleNameFieldLabel": "ルール名", + "xpack.stackConnectors.components.swimlane.selectMessageText": "Swimlaneでレコードを作成", + "xpack.stackConnectors.components.swimlane.severityFieldLabel": "深刻度", + "xpack.stackConnectors.components.teams.connectorTypeTitle": "メッセージを Microsoft Teams チャネルに送信します。", + "xpack.stackConnectors.components.teams.error.invalidWebhookUrlText": "Web フック URL が無効です。", + "xpack.stackConnectors.components.teams.error.requiredMessageText": "メッセージが必要です。", + "xpack.stackConnectors.components.teams.error.webhookUrlTextLabel": "Web フック URL", + "xpack.stackConnectors.components.teams.messageTextAreaFieldLabel": "メッセージ", + "xpack.stackConnectors.components.teams.selectMessageText": "メッセージを Microsoft Teams チャネルに送信します。", + "xpack.stackConnectors.components.teams.webhookUrlHelpLabel": "Microsoft Teams Web フック URL を作成", + "xpack.stackConnectors.components.webhook.connectorTypeTitle": "Web フックデータ", + "xpack.stackConnectors.components.webhook.addHeaderButtonLabel": "ヘッダーを追加", + "xpack.stackConnectors.components.webhook.authenticationLabel": "認証", + "xpack.stackConnectors.components.webhook.bodyCodeEditorAriaLabel": "コードエディター", + "xpack.stackConnectors.components.webhook.bodyFieldLabel": "本文", + "xpack.stackConnectors.components.webhook.error.invalidUrlTextField": "URL が無効です。", + "xpack.stackConnectors.components.webhook.hasAuthSwitchLabel": "この Web フックの認証が必要です", + "xpack.stackConnectors.components.webhook.headerKeyTextFieldLabel": "キー", + "xpack.stackConnectors.components.webhook.headerValueTextFieldLabel": "値", + "xpack.stackConnectors.components.webhook.methodTextFieldLabel": "メソド", + "xpack.stackConnectors.components.webhook.passwordTextFieldLabel": "パスワード", + "xpack.stackConnectors.components.webhook.removeHeaderIconLabel": "キー", + "xpack.stackConnectors.components.webhook.selectMessageText": "Web サービスにリクエストを送信してください。", + "xpack.stackConnectors.components.webhook.urlTextFieldLabel": "URL", + "xpack.stackConnectors.components.webhook.userTextFieldLabel": "ユーザー名", + "xpack.stackConnectors.components.webhook.viewHeadersSwitch": "HTTP ヘッダーを追加", + "xpack.stackConnectors.components.xmatters.connectorTypeTitle": "xMattersデータ", + "xpack.stackConnectors.components.xmatters.authenticationLabel": "認証", + "xpack.stackConnectors.components.xmatters.basicAuthButtonGroupLegend": "基本認証", + "xpack.stackConnectors.components.xmatters.basicAuthLabel": "基本認証", + "xpack.stackConnectors.components.xmatters.connectorSettingsLabel": "xMattersトリガーを設定するときに使用される認証方法を選択します。", + "xpack.stackConnectors.components.xmatters.error.invalidUrlTextField": "URL が無効です。", + "xpack.stackConnectors.components.xmatters.error.invalidUsernameTextField": "ユーザー名が無効です。", + "xpack.stackConnectors.components.xmatters.error.requiredUrlText": "URL が必要です。", + "xpack.stackConnectors.components.xmatters.initiationUrlHelpText": "完全なxMatters URLを含めます。", + "xpack.stackConnectors.components.xmatters.passwordTextFieldLabel": "パスワード", + "xpack.stackConnectors.components.xmatters.selectMessageText": "xMattersワークフローをトリガーします。", + "xpack.stackConnectors.components.xmatters.severity": "深刻度", + "xpack.stackConnectors.components.xmatters.severitySelectCriticalOptionLabel": "重大", + "xpack.stackConnectors.components.xmatters.severitySelectHighOptionLabel": "高", + "xpack.stackConnectors.components.xmatters.severitySelectLowOptionLabel": "低", + "xpack.stackConnectors.components.xmatters.severitySelectMediumOptionLabel": "中", + "xpack.stackConnectors.components.xmatters.severitySelectMinimalOptionLabel": "最小", + "xpack.stackConnectors.components.xmatters.tags": "タグ", + "xpack.stackConnectors.components.xmatters.urlAuthLabel": "URL認証", + "xpack.stackConnectors.components.xmatters.urlLabel": "開始URL", + "xpack.stackConnectors.components.xmatters.userCredsLabel": "ユーザー認証情報", + "xpack.stackConnectors.components.xmatters.userTextFieldLabel": "ユーザー名", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningDesc": "コメントを外部で共有するには、コネクターの[コメントURLを作成]および[コメントオブジェクトを作成]フィールドを構成します。", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningTitle": "ケースコメントを共有できません", + "xpack.stackConnectors.components.serviceNow.deprecatedTooltipTitle": "廃止予定のコネクター", + "xpack.stackConnectors.components.casesWebhook.error.requiredAuthUserNameText": "ユーザー名が必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentMethodText": "コメントメソッドを作成は必須です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentResponseKeyText": "ケース対応の作成ケースIDキーが必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateMethodText": "ケースメソッドの作成は必須です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseCreatedKeyText": "ケース対応の取得の作成日キーが必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseExternalTitleKeyText": "ケース対応の取得の外部タイトルキーが必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseUpdatedKeyText": "ケース対応の取得の更新日キーが必要です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentViewUrlKeyText": "ケースURLの表示は必須です。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateMethodText": "ケースメソッドの更新は必須です。", + "xpack.stackConnectors.components.webhook.error.requiredAuthUserNameText": "ユーザー名が必要です。", + "xpack.stackConnectors.components.webhook.error.requiredMethodText": "メソッドが必要です。", + "xpack.stackConnectors.components.email.addBccButton": "Bcc", + "xpack.stackConnectors.components.email.addCcButton": "Cc", + "xpack.stackConnectors.components.email.authenticationLabel": "認証", + "xpack.stackConnectors.components.email.clientIdFieldLabel": "クライアントID", + "xpack.stackConnectors.components.email.clientSecretTextFieldLabel": "クライアントシークレット", + "xpack.stackConnectors.components.email.fromTextFieldLabel": "送信元", + "xpack.stackConnectors.components.email.hasAuthSwitchLabel": "このサーバーの認証が必要です", + "xpack.stackConnectors.components.email.hostTextFieldLabel": "ホスト", + "xpack.stackConnectors.components.email.messageTextAreaFieldLabel": "メッセージ", + "xpack.stackConnectors.components.email.passwordFieldLabel": "パスワード", + "xpack.stackConnectors.components.email.portTextFieldLabel": "ポート", + "xpack.stackConnectors.components.email.recipientBccTextFieldLabel": "Bcc", + "xpack.stackConnectors.components.email.recipientCopyTextFieldLabel": "Cc", + "xpack.stackConnectors.components.email.recipientTextFieldLabel": "終了:", + "xpack.stackConnectors.components.email.secureSwitchLabel": "セキュア", + "xpack.stackConnectors.components.email.serviceTextFieldLabel": "サービス", + "xpack.stackConnectors.components.email.subjectTextFieldLabel": "件名", + "xpack.stackConnectors.components.email.tenantIdFieldLabel": "テナントID", + "xpack.stackConnectors.components.email.userTextFieldLabel": "ユーザー名", + "xpack.stackConnectors.components.index.resetDefaultIndexLabel": "デフォルトのインデックスをリセット", "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldCandidates": "{fieldCandidatesCount, plural, other {# 個のフィールド候補}}が特定されました。", - "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldValuePairs": "{fieldValuePairsCount, plural, other {# 個の重要なフィールド/値のペア}}が特定されました。", - "xpack.aiops.index.dataLoader.internalServerErrorMessage": "インデックス {index} のデータの読み込み中にエラーが発生。{message}。リクエストがタイムアウトした可能性があります。小さなサンプルサイズを使うか、時間範囲を狭めてみてください。", "xpack.aiops.index.dataViewNotBasedOnTimeSeriesNotificationTitle": "データビュー{dataViewTitle}は時系列に基づいていません", "xpack.aiops.index.errorLoadingDataMessage": "インデックス {index} のデータの読み込み中にエラーが発生。{message}。", "xpack.aiops.progressTitle": "進行状況:{progress}% — {progressMessage}", @@ -31804,20 +32178,7 @@ "xpack.triggersActionsUI.actionVariables.legacyTagsLabel": "{variable}の導入により、これは廃止される予定です。", "xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "このコネクターには {minimumLicenseRequired} ライセンスが必要です。", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "このルールタイプには{minimumLicenseRequired}ライセンスが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.missingVariables": "必須の{variableCount, plural, other {個の変数}}が見つかりません:{variables}", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "アラート履歴インデックスの先頭は\"{alertHistoryPrefix}\"でなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidEmail": "電子メールアドレス{email}が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.notAllowed": "電子メールアドレス{email}が許可されていません。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexHelpText": "ドキュメントは{alertHistoryIndex}インデックスにインデックスされます。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueMessage": "ID {id}の問題を取得できません", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "機密情報はインポートされません。次のフィールド{encryptedFieldsLength, plural, other {}}の値{encryptedFieldsLength, plural, other {}} {secretFieldsLabel}を入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp": "タイムスタンプは、{nowShortFormat}や{nowLongFormat}などの有効な日付でなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "アプリケーション情報の取得を試みるときの受信ステータス:{status}", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateSuccessToastTitle": "{connectorName}コネクターが更新されました", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.apiUrlHelpLabel": "任意のServiceNowインスタンスへの完全なURLを入力します。ない場合は、{instance}。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.serviceNowAppRunning": "更新を実行する前に、ServiceNowアプリストアからElasticアプリをインストールする必要があります。アプリをインストールするには、{visitLink}", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "id {id}のアプリケーションフィールドを取得できません", "xpack.triggersActionsUI.components.buttonGroupField.error.requiredField": "{label}が必要です。", "xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "{numErrors, number} {numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}を削除できませんでした", "xpack.triggersActionsUI.components.deleteSelectedIdsSuccessNotification.descriptionText": "{numSuccesses, number} {numSuccesses, plural, one {{singleTitle}} other {{multipleTitle}}}を削除しました", @@ -31972,340 +32333,6 @@ "xpack.triggersActionsUI.components.addMessageVariables.addRuleVariableTitle": "ルール変数を追加", "xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton": "変数を追加", "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "アラート検索でエラーが発生しました", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.commentsTextAreaFieldLabel": "追加のコメント", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.descriptionTextAreaFieldLabel": "説明", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.tagsFieldLabel": "タグ", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.titleFieldLabel": "概要(必須)", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.actionTypeTitle": "Webフック - ケース管理データ", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addHeaderButton": "追加", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addVariable": "変数を追加", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.authenticationLabel": "認証", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseCommentDesc": "Kibanaケースコメント", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseDescriptionDesc": "Kibanaケース説明", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTagsDesc": "Kibanaケースタグ", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTitleDesc": "Kibanaケースタイトル", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonHelp": "コメントを作成するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonTextFieldLabel": "コメントオブジェクトを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentMethodTextFieldLabel": "コメントメソッドを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlHelp": "コメントをケースに追加するためのAPI URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlTextFieldLabel": "コメントURLを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonHelpText": "ケースを作成するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonTextFieldLabel": "ケースオブジェクトを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentMethodTextFieldLabel": "ケースメソッドを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyHelpText": "外部ケースIDを含むケース対応の作成のJSONキー", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyTextFieldLabel": "ケース対応ケースキーを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentUrlTextFieldLabel": "ケースURLを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.deleteHeaderButton": "削除", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.docLink": "Webフックの構成 - ケース管理コネクター。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentIncidentText": "コメントオブジェクトの作成は有効なJSONでなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentUrlText": "コメントURLの作成はURL形式でなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateIncidentText": "ケースオブジェクトの作成は必須であり、有効なJSONでなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateUrlText": "ケースURLの作成は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredGetIncidentUrlText": "ケースURLの取得は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateIncidentText": "ケースオブジェクトの更新は必須であり、有効なJSONでなければなりません。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateUrlText": "ケースURLの更新は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalIdDesc": "外部システムID", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalTitleDesc": "外部システムタイトル", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyHelp": "外部ケースタイトルを含むケース対応の取得のJSONキー", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyTextFieldLabel": "ケース対応の取得の外部タイトルキー", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlHelp": "外部システムからケース詳細JSONを取得するAPI URL。変数セレクターを使用して、外部システムIDをURLに追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlTextFieldLabel": "ケースURLを取得", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.hasAuthSwitchLabel": "この Web フックの認証が必要です", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.httpHeadersTitle": "使用中のヘッダー", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonCodeEditorAriaLabel": "コードエディター", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonFieldLabel": "JSON", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.keyTextFieldLabel": "キー", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.next": "次へ", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.passwordTextFieldLabel": "パスワード", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.previous": "前へ", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.selectMessageText": "ケース管理Webサービスにリクエストを送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step1": "コネクターを設定", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2": "ケースを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2Description": "外部システムでケースを作成するフィールドを設定します。必要なフィールドを判断するには、サービスのAPIドキュメントを確認してください", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3": "ケース情報を取得", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3Description": "外部システムでコメントをケースに追加するフィールドを設定します。一部のシステムでは、ケースでの更新の作成と同じメソッドの場合があります。必要なフィールドを判断するには、サービスのAPIドキュメントを確認してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4": "コメントと更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4a": "ケースで更新を作成", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4aDescription": "外部システムでケースの更新を作成するフィールドを設定します。一部のシステムでは、ケースへのコメントの追加と同じメソッドの場合があります。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4b": "ケースでコメントを追加", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4bDescription": "外部システムでコメントをケースに追加するフィールドを設定します。一部のシステムでは、ケースでの更新の作成と同じメソッドの場合があります。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonHelpl": "ケースを更新するJSONオブジェクト。変数セレクターを使用して、ケースデータをペイロードに追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonTextFieldLabel": "ケースオブジェクトを更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentMethodTextFieldLabel": "ケースメソッドを更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlHelp": "ケースを更新するAPI URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlTextFieldLabel": "ケースURLを更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.userTextFieldLabel": "ユーザー名", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.valueTextFieldLabel": "値", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewHeadersSwitch": "HTTP ヘッダーを追加", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlHelp": "外部システムでケースを表示するURL。変数セレクターを使用して、外部システムIDまたは外部システムタイトルをURLに追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlTextFieldLabel": "外部ケース表示URL", - "xpack.triggersActionsUI.components.builtinActionTypes.common.requiredShortDescTextField": "短い説明が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientIdHelpLabel": "クライアントIDの構成", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientSecretHelpLabel": "クライアントシークレットの構成", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.tenantIdHelpLabel": "テナントIDの構成", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "メールに送信", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.amazonSesServerTypeLabel": "Amazon SES", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel": "電子メールアカウントの構成", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.elasticCloudServerTypeLabel": "Elastic Cloud", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.exchangeServerTypeLabel": "MS Exchange Server", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "その他", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "サーバーからメールを送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.updateErrorNotificationText": "サービス構成を取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "アラート履歴インデックスには有効なサフィックスを含める必要があります。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidPortText": "ポートが無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "ユーザー名が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "クライアントIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "ドキュメントが必要です。有効なJSONオブジェクトにしてください。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "To、Cc、または Bcc のエントリーがありません。 1 つ以上のエントリーが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "送信元が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "ホストが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "メッセージが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "ポートが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "メッセージが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "サービスは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "メッセージが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "件名が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "テナントIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "本文が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookSummaryText": "タイトルが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "データをインデックスする", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "選択…", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.configureIndexHelpLabel": "インデックスコネクターを構成しています。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.connectorSectionTitle": "インデックスを書き出す", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "この時間フィールドをドキュメントにインデックスが作成された時刻に設定します。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "各ドキュメントの時刻フィールドを定義", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "インデックスするドキュメント", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.notValidIndexText": "インデックスは有効ではありません。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "時間フィールド", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "インデックスドキュメントの例。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "データビューに基づく", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesToQueryLabel": "インデックス", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel": "コードエディター", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndex": "Elasticsearchインデックス", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexDocLink": "ドキュメントを表示します。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshLabel": "更新インデックス", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshTooltip": "影響を受けるシャードを更新し、この処理を検索できるようにします。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText": "データを Elasticsearch にインデックスしてください。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "APIトークン", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "追加のコメント", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "説明", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "メールアドレス", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "ラベル", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "ラベルにはスペースを使用できません。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "親問題", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "プロジェクトキー", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "概要が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "入力して検索", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "入力して検索", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "読み込み中...", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.selectMessageText": "Jira でインシデントを作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.severitySelectFieldLabel": "優先度", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.summaryFieldLabel": "概要(必須)", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetFieldsMessage": "フィールドを取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "問題を取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "問題タイプを取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "問題タイプ", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "PagerDuty に送信", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlInvalid": "無効なAPI URL", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "API URL(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "クラス(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.componentTextFieldLabel": "コンポーネント(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextFieldLabel": "DedupKey(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextRequiredFieldLabel": "DedupKey", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredDedupKeyText": "インシデントを解決または確認するときには、DedupKeyが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredRoutingKeyText": "統合キー/ルーティングキーが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredSummaryText": "概要が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventActionSelectFieldLabel": "イベントアクション", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectAcknowledgeOptionLabel": "承認", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "解決", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "トリガー", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "グループ(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "PagerDuty アカウントを構成します", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "統合キー", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "PagerDuty でイベントを送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectCriticalOptionLabel": "重大", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectErrorOptionLabel": "エラー", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectFieldLabel": "重要度(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectInfoOptionLabel": "情報", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectWarningOptionLabel": "警告", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "ソース(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "まとめ", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "タイムスタンプ(任意)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Resilient", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "APIキーID", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "APIキーシークレット", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "追加のコメント", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "説明", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "名前(必須)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "組織 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "名前が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "IBM Resilient でインシデントを作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "深刻度", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "インシデントタイプを取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetSeverityMessage": "深刻度を取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.urgencySelectFieldLabel": "インシデントタイプ", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle": "サーバーログに送信", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logLevelFieldLabel": "レベル", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "メッセージ", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "Kibana ログにメッセージを追加します。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "ServiceNowインスタンスURL", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "Elastic ServiceNowアプリがインストールされていません", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.content": "ServiceNowアプリストアに移動し、アプリケーションをインストールしてください", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.errorMessage": "エラーメッセージ", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.authenticationLabel": "認証", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.cancelButtonText": "キャンセル", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.categoryTitle": "カテゴリー", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientIdTextFieldLabel": "クライアントID", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientSecretTextFieldLabel": "クライアントシークレット", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.commentsTextAreaFieldLabel": "追加のコメント", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.confirmButtonText": "更新", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationDisplay": "相関関係表示(オプション)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationID": "相関関係ID(オプション)", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutCreate": "または新規作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate": "このコネクターを更新します", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutTitle": "このコネクターは廃止予定です", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "説明", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "ソースインスタンス", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.fetchErrorMsg": "取得できませんでした。ServiceNowインスタンスのURLまたはCORS公正を確認します。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "インパクト", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "このコネクターを使用するには、まずServiceNowアプリストアからElasticアプリをインストールします。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.keyIdTextFieldLabel": "JWT VerifierキーID", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "メッセージキー", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "メトリック名", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "ノード", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "パスワード", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "優先度", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassLabelHelpText": "これは、秘密鍵でパスワードを設定した場合にのみ必要です", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassTextFieldLabel": "秘密鍵パスワード", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyTextFieldLabel": "秘密鍵", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredClientIdTextField": "クライアントIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField": "JWT VerifierキーIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPrivateKeyTextField": "秘密鍵は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "重要度は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserIdentifierTextField": "ユーザーIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "ユーザー名が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "リソース", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "開発者インスタンスを設定", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "重要度(必須)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severitySelectFieldLabel": "深刻度", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.snInstanceLabel": "ServiceNowインスタンス", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.sourceTextAreaFieldLabel": "送信元", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.subcategoryTitle": "サブカテゴリー", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.title": "インシデント", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.titleFieldLabel": "短い説明(必須)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.typeTextAreaFieldLabel": "型", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unableToGetChoicesMessage": "選択肢を取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unknown": "不明", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateCalloutText": "コネクターが更新されました。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormCredentialsTitle": "認証資格情報を入力", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormInstallTitle": "Elastic ServiceNowアプリをインストール", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle": "ServiceNowコネクターを更新", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormUrlTitle": "ServiceNowインスタンスURLを入力", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.urgencySelectFieldLabel": "緊急", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.useOAuth": "OAuth認証を使用", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.userEmailTextFieldLabel": "ユーザーID", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.usernameTextFieldLabel": "ユーザー名", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.visitSNStore": "ServiceNowアプリストアにアクセス", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.warningMessage": "このコネクターのすべてのインスタンスが更新され、元に戻せません。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.correlationIDHelpLabel": "インシデントを更新するID", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.actionTypeTitle": "ServiceNow ITOM", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowITOM.event": "イベント", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.selectMessageText": "ServiceNow ITOMでイベントを作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.actionTypeTitle": "ServiceNow ITSM", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.selectMessageText": "ServiceNow ITSMでインシデントを作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.actionTypeTitle": "ServiceNow SecOps", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.selectMessageText": "ServiceNow SecOpsでインシデントを作成します。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowSIR.title": "セキュリティインシデント", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "インシデントを更新するID", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "Slack に送信", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "Web フック URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "メッセージ", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "Slack チャネルにメッセージを送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "Slack Web フック URL を作成", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Web フック URL", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationFieldsMessage": "アプリケーションフィールドを取得できません", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "Swimlaneレコードを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "アラートID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "Swimlane APIトークンを指定", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "APIトークン", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "API Url", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.appIdTextFieldLabel": "アプリケーションID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseIdFieldLabel": "ケースID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseNameFieldLabel": "ケース名", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.commentsFieldLabel": "コメント", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.configureConnectionLabel": "API接続を構成", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.connectorType": "コネクタータイプ", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.descriptionFieldLabel": "説明", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "このコネクターを選択できません。必要なアラートフィールドマッピングがありません。このコネクターを編集して、必要なフィールドマッピングを追加するか、タイプがアラートのコネクターを選択できます。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "このコネクターにはフィールドマッピングがありません。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "アラートIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "アプリIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "ケースIDは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "ケース名は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "コメントは必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "説明が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "ルール名は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "重要度は必須です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "フィールドマッピングを構成", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "次へ", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "フィールドマッピングが構成されていない場合、Swimlaneコネクタータイプはすべてに設定されます。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "戻る", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "ルール名", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "Swimlaneでレコードを作成", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "深刻度", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "メッセージを Microsoft Teams チャネルに送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "Web フック URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "メッセージが必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.webhookUrlTextLabel": "Web フック URL", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "メッセージ", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "メッセージを Microsoft Teams チャネルに送信します。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "Microsoft Teams Web フック URL を作成", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Web フックデータ", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButtonLabel": "ヘッダーを追加", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "認証", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "コードエディター", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "本文", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "この Web フックの認証が必要です", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerKeyTextFieldLabel": "キー", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerValueTextFieldLabel": "値", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "メソド", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "パスワード", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.removeHeaderIconLabel": "キー", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "Web サービスにリクエストを送信してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "ユーザー名", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "HTTP ヘッダーを追加", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "xMattersデータ", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "認証", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthButtonGroupLegend": "基本認証", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "基本認証", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "xMattersトリガーを設定するときに使用される認証方法を選択します。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "URL が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUsernameTextField": "ユーザー名が無効です。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "URL が必要です。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "完全なxMatters URLを含めます。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "パスワード", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "xMattersワークフローをトリガーします。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "深刻度", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "重大", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectHighOptionLabel": "高", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectLowOptionLabel": "低", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMediumOptionLabel": "中", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMinimalOptionLabel": "最小", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.tags": "タグ", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlAuthLabel": "URL認証", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlLabel": "開始URL", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userCredsLabel": "ユーザー認証情報", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userTextFieldLabel": "ユーザー名", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorButtonLabel": "コネクターを作成", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyBody": "Kibanaで実行するメール、Slack、Elasticsearch、およびサードパーティサービスを構成します。", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyTitle": "初めてのコネクターを作成する", @@ -32327,8 +32354,6 @@ "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorMessage": "エディターが見つかりませんでした。ページを更新して再試行してください", "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorTitle": "メッセージ変数を追加できません", "xpack.triggersActionsUI.components.simpleConnectorForm.secrets.authenticationLabel": "認証", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningDesc": "コメントを外部で共有するには、コネクターの[コメントURLを作成]および[コメントオブジェクトを作成]フィールドを構成します。", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningTitle": "ケースコメントを共有できません", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "コネクター", "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]が[dateEnd]よりも大です", "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]:[dateStart]が[dateEnd]と等しくない場合に指定する必要があります", @@ -32401,7 +32426,6 @@ "xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "コネクター設定を読み込んでいます...", "xpack.triggersActionsUI.sections.actionForm.actionSectionsTitle": "アクション", "xpack.triggersActionsUI.sections.actionForm.addActionButtonLabel": "アクションの追加", - "xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle": "廃止予定のコネクター", "xpack.triggersActionsUI.sections.actionForm.getMoreConnectorsTitle": "その他のコネクターを取得", "xpack.triggersActionsUI.sections.actionForm.getMoreRuleTypesTitle": "その他のルールタイプを取得", "xpack.triggersActionsUI.sections.actionForm.incidentManagementSystemLabel": "インシデント管理システム", @@ -32440,17 +32464,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.actionErrorToolTip": "アクションにはエラーがあります。", "xpack.triggersActionsUI.sections.actionTypeForm.actionRunWhenInActionGroup": "次のときに実行", "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "コネクターの追加", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredAuthUserNameText": "ユーザー名が必要です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateCommentMethodText": "コメントメソッドを作成は必須です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateIncidentResponseKeyText": "ケース対応の作成ケースIDキーが必要です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateMethodText": "ケースメソッドの作成は必須です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseCreatedKeyText": "ケース対応の取得の作成日キーが必要です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseExternalTitleKeyText": "ケース対応の取得の外部タイトルキーが必要です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseUpdatedKeyText": "ケース対応の取得の更新日キーが必要です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentViewUrlKeyText": "ケースURLの表示は必須です。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredUpdateMethodText": "ケースメソッドの更新は必須です。", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "ユーザー名が必要です。", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "メソッドが必要です。", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "コネクターを選択", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました", "xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "キャンセル", @@ -32460,25 +32473,6 @@ "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "理由", "xpack.triggersActionsUI.sections.alertsTable.column.actions": "アクション", "xpack.triggersActionsUI.sections.alertsTable.leadingControl.viewDetails": "詳細を表示", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "Bcc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "Cc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "認証", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientIdFieldLabel": "クライアントID", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientSecretTextFieldLabel": "クライアントシークレット", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel": "送信元", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel": "このサーバーの認証が必要です", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel": "ホスト", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.messageTextAreaFieldLabel": "メッセージ", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel": "パスワード", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel": "ポート", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel": "Bcc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel": "Cc", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel": "終了:", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.secureSwitchLabel": "セキュア", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.serviceTextFieldLabel": "サービス", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel": "件名", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.tenantIdFieldLabel": "テナントID", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel": "ユーザー名", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseCancelButtonText": "キャンセル", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseConfirmButtonText": "変更を破棄", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseMessage": "保存されていない変更は回復できません。", @@ -32668,7 +32662,6 @@ "xpack.triggersActionsUI.sections.rulesList.removeAllButton": "すべて削除", "xpack.triggersActionsUI.sections.rulesList.removeCancelButton": "キャンセル", "xpack.triggersActionsUI.sections.rulesList.removeConfirmButton": "すべて削除", - "xpack.triggersActionsUI.sections.rulesList.resetDefaultIndexLabel": "デフォルトのインデックスをリセット", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDecrypting": "ルールの復号中にエラーが発生しました。", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDisabled": "ルールを実行できませんでした。ルールは無効化された後に実行されました。", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonLicense": "ルールを実行できません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0f15a16ae14a..5ade45168ae7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6378,6 +6378,383 @@ "xpack.stackConnectors.xmatters.shouldNotHaveSecretsUrl": "usesBasic 为 true 时不得提供 secretsUrl", "xpack.stackConnectors.xmatters.shouldNotHaveUsernamePassword": "usesBasic 为 false 时不得提供用户名和密码", "xpack.stackConnectors.xmatters.title": "xMatters", + "xpack.stackConnectors.components.casesWebhook.error.missingVariables": "缺少所需{variableCount, plural, other {变量}}:{variables}", + "xpack.stackConnectors.components.index.error.badIndexOverrideValue": "告警历史记录索引必须以“{alertHistoryPrefix}”开头。", + "xpack.stackConnectors.components.email.error.invalidEmail": "电子邮件地址 {email} 无效。", + "xpack.stackConnectors.components.email.error.notAllowed": "不允许使用电子邮件地址 {email}。", + "xpack.stackConnectors.components.index.preconfiguredIndexHelpText": "文档已索引到 {alertHistoryIndex} 索引中。", + "xpack.stackConnectors.components.jira.unableToGetIssueMessage": "无法获取 ID 为 {id} 的问题", + "xpack.stackConnectors.components.pagerDuty.error.invalidTimestamp": "时间戳必须是有效的日期,例如 {nowShortFormat} 或 {nowLongFormat}。", + "xpack.stackConnectors.components.serviceNow.apiInfoError": "尝试获取应用程序信息时收到的状态:{status}", + "xpack.stackConnectors.components.serviceNow.appInstallationInfo": "{update} {create} ", + "xpack.stackConnectors.components.serviceNow.updateSuccessToastTitle": "{connectorName} 连接器已更新", + "xpack.stackConnectors.components.serviceNow.apiUrlHelpLabel": "提供所需 ServiceNow 实例的完整 URL。如果没有,{instance}。", + "xpack.stackConnectors.components.serviceNow.appRunning": "在运行更新之前,必须从 ServiceNow 应用商店安装 Elastic 应用。{visitLink} 以安装该应用", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationMessage": "无法获取 ID 为 {id} 的应用程序", + "xpack.stackConnectors.components.casesWebhook.commentsTextAreaFieldLabel": "其他注释", + "xpack.stackConnectors.components.casesWebhook.descriptionTextAreaFieldLabel": "描述", + "xpack.stackConnectors.components.casesWebhook.tagsFieldLabel": "标签", + "xpack.stackConnectors.components.casesWebhook.titleFieldLabel": "摘要(必填)", + "xpack.stackConnectors.components.casesWebhook.addHeaderButton": "添加", + "xpack.stackConnectors.components.casesWebhook.addVariable": "添加变量", + "xpack.stackConnectors.components.casesWebhook.authenticationLabel": "身份验证", + "xpack.stackConnectors.components.casesWebhook.caseCommentDesc": "Kibana 案例注释", + "xpack.stackConnectors.components.casesWebhook.caseDescriptionDesc": "Kibana 案例描述", + "xpack.stackConnectors.components.casesWebhook.caseTagsDesc": "Kibana 案例标签", + "xpack.stackConnectors.components.casesWebhook.caseTitleDesc": "Kibana 案例标题", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonHelp": "用于创建注释的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", + "xpack.stackConnectors.components.casesWebhook.createCommentJsonTextFieldLabel": "创建注释对象", + "xpack.stackConnectors.components.casesWebhook.createCommentMethodTextFieldLabel": "创建注释方法", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlHelp": "用于添加注释到案例的 API URL。", + "xpack.stackConnectors.components.casesWebhook.createCommentUrlTextFieldLabel": "创建注释 URL", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonHelpText": "用于创建案例的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", + "xpack.stackConnectors.components.casesWebhook.createIncidentJsonTextFieldLabel": "创建案例对象", + "xpack.stackConnectors.components.casesWebhook.createIncidentMethodTextFieldLabel": "创建案例方法", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyHelpText": "创建案例响应中包含外部案例 ID 的 JSON 键", + "xpack.stackConnectors.components.casesWebhook.createIncidentResponseKeyTextFieldLabel": "创建案例响应案例键", + "xpack.stackConnectors.components.casesWebhook.createIncidentUrlTextFieldLabel": "创建案例 URL", + "xpack.stackConnectors.components.casesWebhook.deleteHeaderButton": "删除", + "xpack.stackConnectors.components.casesWebhook.docLink": "正在配置 Webhook - 案例管理连接器。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentIncidentText": "创建注释对象必须为有效 JSON。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentUrlText": "创建注释 URL 必须为 URL 格式。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentText": "“创建案例对象”必填并且必须为有效 JSON。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateUrlText": "“创建案例 URL”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentUrlText": "“获取案例 URL”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateIncidentText": "“更新案例对象”必填并且必须为有效 JSON。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateUrlText": "“更新案例 URL”必填。", + "xpack.stackConnectors.components.casesWebhook.externalIdDesc": "外部系统 ID", + "xpack.stackConnectors.components.casesWebhook.externalTitleDesc": "外部系统标题", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyHelp": "获取案例响应中包含外部案例标题的 JSON 键", + "xpack.stackConnectors.components.casesWebhook.getIncidentResponseExternalTitleKeyTextFieldLabel": "获取案例响应外部标题键", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlHelp": "用于从外部系统中获取案例详情 JSON 的 API URL。使用变量选择器添加外部系统 ID 到 URL。", + "xpack.stackConnectors.components.casesWebhook.getIncidentUrlTextFieldLabel": "获取案例 URL", + "xpack.stackConnectors.components.casesWebhook.hasAuthSwitchLabel": "此 Webhook 需要身份验证", + "xpack.stackConnectors.components.casesWebhook.httpHeadersTitle": "在用的标头", + "xpack.stackConnectors.components.casesWebhook.jsonCodeEditorAriaLabel": "代码编辑器", + "xpack.stackConnectors.components.casesWebhook.jsonFieldLabel": "JSON", + "xpack.stackConnectors.components.casesWebhook.keyTextFieldLabel": "钥匙", + "xpack.stackConnectors.components.casesWebhook.next": "下一步", + "xpack.stackConnectors.components.casesWebhook.passwordTextFieldLabel": "密码", + "xpack.stackConnectors.components.casesWebhook.previous": "上一步", + "xpack.stackConnectors.components.casesWebhook.selectMessageText": "发送请求到案例管理 Web 服务。", + "xpack.stackConnectors.components.casesWebhook.step1": "设置连接器", + "xpack.stackConnectors.components.casesWebhook.step2": "创建案例", + "xpack.stackConnectors.components.casesWebhook.step2Description": "设置字段以在外部系统中创建案例。查阅您服务的 API 文档以了解需要哪些字段", + "xpack.stackConnectors.components.casesWebhook.step3": "获取案例信息", + "xpack.stackConnectors.components.casesWebhook.step3Description": "设置字段以在外部系统中添加案例注释。对于某些系统,这可能采取与在案例中创建更新相同的方法。查阅您服务的 API 文档以了解需要哪些字段。", + "xpack.stackConnectors.components.casesWebhook.step4": "注释和更新", + "xpack.stackConnectors.components.casesWebhook.step4a": "在案例中创建更新", + "xpack.stackConnectors.components.casesWebhook.step4aDescription": "设置字段以在外部系统中创建案例更新。对于某些系统,这可能采取与添加案例注释相同的方法。", + "xpack.stackConnectors.components.casesWebhook.step4b": "在案例中添加注释", + "xpack.stackConnectors.components.casesWebhook.step4bDescription": "设置字段以在外部系统中添加案例注释。对于某些系统,这可能采取与在案例中创建更新相同的方法。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonHelpl": "用于更新案例的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentJsonTextFieldLabel": "更新案例对象", + "xpack.stackConnectors.components.casesWebhook.updateIncidentMethodTextFieldLabel": "更新案例方法", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlHelp": "用于更新案例的 API URL。", + "xpack.stackConnectors.components.casesWebhook.updateIncidentUrlTextFieldLabel": "更新案例 URL", + "xpack.stackConnectors.components.casesWebhook.userTextFieldLabel": "用户名", + "xpack.stackConnectors.components.casesWebhook.valueTextFieldLabel": "值", + "xpack.stackConnectors.components.casesWebhook.viewHeadersSwitch": "添加 HTTP 标头", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlHelp": "用于查看外部系统中的案例的 URL。使用变量选择器添加外部系统 ID 或外部系统标题到 URL。", + "xpack.stackConnectors.components.casesWebhook.viewIncidentUrlTextFieldLabel": "外部案例查看 URL", + "xpack.stackConnectors.components.serviceNow.requiredShortDescTextField": "“简短描述”必填。", + "xpack.stackConnectors.components.email.exchangeForm.clientIdHelpLabel": "配置客户端 ID", + "xpack.stackConnectors.components.email.exchangeForm.clientSecretHelpLabel": "配置客户端密钥", + "xpack.stackConnectors.components.email.exchangeForm.tenantIdHelpLabel": "配置租户 ID", + "xpack.stackConnectors.components.email.connectorTypeTitle": "发送到电子邮件", + "xpack.stackConnectors.components.email.amazonSesServerTypeLabel": "Amazon SES", + "xpack.stackConnectors.components.email.configureAccountsHelpLabel": "配置电子邮件帐户", + "xpack.stackConnectors.components.email.elasticCloudServerTypeLabel": "Elastic Cloud", + "xpack.stackConnectors.components.email.exchangeServerTypeLabel": "MS Exchange Server", + "xpack.stackConnectors.components.email.gmailServerTypeLabel": "Gmail", + "xpack.stackConnectors.components.email.otherServerTypeLabel": "其他", + "xpack.stackConnectors.components.email.outlookServerTypeLabel": "Outlook", + "xpack.stackConnectors.components.email.selectMessageText": "从您的服务器发送电子邮件。", + "xpack.stackConnectors.components.email.updateErrorNotificationText": "无法获取服务配置", + "xpack.stackConnectors.components.index.error.badIndexOverrideSuffix": "告警历史记录索引必须包含有效的后缀。", + "xpack.stackConnectors.components.email.error.invalidPortText": "端口无效。", + "xpack.stackConnectors.components.email.error.requiredAuthUserNameText": "“用户名”必填。", + "xpack.stackConnectors.components.email.error.requiredClientIdText": "“客户端 ID”必填。", + "xpack.stackConnectors.components.index.error.requiredDocumentJson": "“文档”必填,并且应为有效的 JSON 对象。", + "xpack.stackConnectors.components.email.error.requiredEntryText": "未输入收件人、抄送、密送。 至少需要输入一个。", + "xpack.stackConnectors.components.email.error.requiredFromText": "“发送者”必填。", + "xpack.stackConnectors.components.email.error.requiredHostText": "“主机”必填。", + "xpack.stackConnectors.components.email.error.requiredMessageText": "“消息”必填。", + "xpack.stackConnectors.components.email.error.requiredPortText": "“端口”必填。", + "xpack.stackConnectors.components.serverLog.error.requiredServerLogMessageText": "“消息”必填。", + "xpack.stackConnectors.components.email.error.requiredServiceText": "“服务”必填。", + "xpack.stackConnectors.components.email.error.requiredSubjectText": "“主题”必填。", + "xpack.stackConnectors.components.email.error.requiredTenantIdText": "“租户 ID”必填。", + "xpack.stackConnectors.components.webhook.error.requiredWebhookBodyText": "“正文”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredWebhookSummaryText": "“标题”必填。", + "xpack.stackConnectors.components.index.connectorTypeTitle": "索引数据", + "xpack.stackConnectors.components.index.configureIndexHelpLabel": "配置索引连接器。", + "xpack.stackConnectors.components.index.connectorSectionTitle": "写入到索引", + "xpack.stackConnectors.components.index.definedateFieldTooltip": "将此时间字段设置为索引文档的时间。", + "xpack.stackConnectors.components.index.defineTimeFieldLabel": "为每个文档定义时间字段", + "xpack.stackConnectors.components.index.documentsFieldLabel": "要索引的文档", + "xpack.stackConnectors.components.index.error.notValidIndexText": "索引无效。", + "xpack.stackConnectors.components.index.executionTimeFieldLabel": "时间字段", + "xpack.stackConnectors.components.index.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。", + "xpack.stackConnectors.components.index.indexDocumentHelpLabel": "索引文档示例。", + "xpack.stackConnectors.components.index.indicesToQueryLabel": "索引", + "xpack.stackConnectors.components.index.jsonDocAriaLabel": "代码编辑器", + "xpack.stackConnectors.components.index.preconfiguredIndex": "Elasticsearch 索引", + "xpack.stackConnectors.components.index.preconfiguredIndexDocLink": "查看文档。", + "xpack.stackConnectors.components.index.refreshLabel": "刷新索引", + "xpack.stackConnectors.components.index.refreshTooltip": "刷新影响的分片以使此操作对搜索可见。", + "xpack.stackConnectors.components.index.selectMessageText": "将数据索引到 Elasticsearch 中。", + "xpack.stackConnectors.components.jira.connectorTypeTitle": "Jira", + "xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "API 令牌", + "xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.jira.commentsTextAreaFieldLabel": "其他注释", + "xpack.stackConnectors.components.jira.descriptionTextAreaFieldLabel": "描述", + "xpack.stackConnectors.components.jira.emailTextFieldLabel": "电子邮件地址", + "xpack.stackConnectors.components.jira.impactSelectFieldLabel": "标签", + "xpack.stackConnectors.components.jira.labelsSpacesErrorMessage": "标签不能包含空格。", + "xpack.stackConnectors.components.jira.parentIssueSearchLabel": "父问题", + "xpack.stackConnectors.components.jira.projectKey": "项目键", + "xpack.stackConnectors.components.jira.requiredSummaryTextField": "“摘要”必填。", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxAriaLabel": "键入内容进行搜索", + "xpack.stackConnectors.components.jira.searchIssuesComboBoxPlaceholder": "键入内容进行搜索", + "xpack.stackConnectors.components.jira.searchIssuesLoading": "正在加载……", + "xpack.stackConnectors.components.jira.selectMessageText": "在 Jira 创建事件。", + "xpack.stackConnectors.components.jira.severitySelectFieldLabel": "优先级", + "xpack.stackConnectors.components.jira.summaryFieldLabel": "摘要(必填)", + "xpack.stackConnectors.components.jira.unableToGetFieldsMessage": "无法获取字段", + "xpack.stackConnectors.components.jira.unableToGetIssuesMessage": "无法获取问题", + "xpack.stackConnectors.components.jira.unableToGetIssueTypesMessage": "无法获取问题类型", + "xpack.stackConnectors.components.jira.urgencySelectFieldLabel": "问题类型", + "xpack.stackConnectors.components.pagerDuty.connectorTypeTitle": "发送到 PagerDuty", + "xpack.stackConnectors.components.pagerDuty.apiUrlInvalid": "API URL 无效", + "xpack.stackConnectors.components.pagerDuty.apiUrlTextFieldLabel": "API URL(可选)", + "xpack.stackConnectors.components.pagerDuty.classFieldLabel": "类(可选)", + "xpack.stackConnectors.components.pagerDuty.componentTextFieldLabel": "组件(可选)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextFieldLabel": "DedupKey(可选)", + "xpack.stackConnectors.components.pagerDuty.dedupKeyTextRequiredFieldLabel": "DedupKey", + "xpack.stackConnectors.components.pagerDuty.error.requiredDedupKeyText": "解决或确认事件时需要 DedupKey。", + "xpack.stackConnectors.components.pagerDuty.error.requiredRoutingKeyText": "需要集成密钥/路由密钥。", + "xpack.stackConnectors.components.pagerDuty.error.requiredSummaryText": "“摘要”必填。", + "xpack.stackConnectors.components.pagerDuty.eventActionSelectFieldLabel": "事件操作", + "xpack.stackConnectors.components.pagerDuty.eventSelectAcknowledgeOptionLabel": "确认", + "xpack.stackConnectors.components.pagerDuty.eventSelectResolveOptionLabel": "解决", + "xpack.stackConnectors.components.pagerDuty.eventSelectTriggerOptionLabel": "触发", + "xpack.stackConnectors.components.pagerDuty.groupTextFieldLabel": "组(可选)", + "xpack.stackConnectors.components.pagerDuty.routingKeyNameHelpLabel": "配置 PagerDuty 帐户", + "xpack.stackConnectors.components.pagerDuty.routingKeyTextFieldLabel": "集成密钥", + "xpack.stackConnectors.components.pagerDuty.selectMessageText": "在 PagerDuty 中发送事件。", + "xpack.stackConnectors.components.pagerDuty.severitySelectCriticalOptionLabel": "紧急", + "xpack.stackConnectors.components.pagerDuty.severitySelectErrorOptionLabel": "错误", + "xpack.stackConnectors.components.pagerDuty.severitySelectFieldLabel": "严重性(可选)", + "xpack.stackConnectors.components.pagerDuty.severitySelectInfoOptionLabel": "信息", + "xpack.stackConnectors.components.pagerDuty.severitySelectWarningOptionLabel": "警告", + "xpack.stackConnectors.components.pagerDuty.sourceTextFieldLabel": "源(可选)", + "xpack.stackConnectors.components.pagerDuty.summaryFieldLabel": "摘要", + "xpack.stackConnectors.components.pagerDuty.timestampTextFieldLabel": "时间戳(可选)", + "xpack.stackConnectors.components.resilient.connectorTypeTitle": "Resilient", + "xpack.stackConnectors.components.resilient.apiKeyId": "API 密钥 ID", + "xpack.stackConnectors.components.resilient.apiKeySecret": "API 密钥密码", + "xpack.stackConnectors.components.resilient.apiUrlTextFieldLabel": "URL", + "xpack.stackConnectors.components.resilient.commentsTextAreaFieldLabel": "其他注释", + "xpack.stackConnectors.components.resilient.descriptionTextAreaFieldLabel": "描述", + "xpack.stackConnectors.components.resilient.nameFieldLabel": "名称(必填)", + "xpack.stackConnectors.components.resilient.orgId": "组织 ID", + "xpack.stackConnectors.components.resilient.requiredNameTextField": "“名称”必填。", + "xpack.stackConnectors.components.resilient.selectMessageText": "在 IBM Resilient 中创建事件。", + "xpack.stackConnectors.components.resilient.severity": "严重性", + "xpack.stackConnectors.components.resilient.unableToGetIncidentTypesMessage": "无法获取事件类型", + "xpack.stackConnectors.components.resilient.unableToGetSeverityMessage": "无法获取严重性", + "xpack.stackConnectors.components.resilient.urgencySelectFieldLabel": "事件类型", + "xpack.stackConnectors.components.serverLog.connectorTypeTitle": "发送到服务器日志", + "xpack.stackConnectors.components.serverLog.logLevelFieldLabel": "级别", + "xpack.stackConnectors.components.serverLog.logMessageFieldLabel": "消息", + "xpack.stackConnectors.components.serverLog.selectMessageText": "将消息添加到 Kibana 日志。", + "xpack.stackConnectors.components.serviceNow.apiUrlTextFieldLabel": "ServiceNow 实例 URL", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout": "未安装 Elastic ServiceNow 应用", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.content": "请前往 ServiceNow 应用商店并安装该应用程序", + "xpack.stackConnectors.components.serviceNow.applicationRequiredCallout.errorMessage": "错误消息", + "xpack.stackConnectors.components.serviceNow.authenticationLabel": "身份验证", + "xpack.stackConnectors.components.serviceNow.cancelButtonText": "取消", + "xpack.stackConnectors.components.serviceNow.categoryTitle": "类别", + "xpack.stackConnectors.components.serviceNow.clientIdTextFieldLabel": "客户端 ID", + "xpack.stackConnectors.components.serviceNow.clientSecretTextFieldLabel": "客户端密钥", + "xpack.stackConnectors.components.serviceNow.commentsTextAreaFieldLabel": "其他注释", + "xpack.stackConnectors.components.serviceNow.confirmButtonText": "更新", + "xpack.stackConnectors.components.serviceNow.correlationDisplay": "相关性显示(可选)", + "xpack.stackConnectors.components.serviceNow.correlationID": "相关性 ID(可选)", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutCreate": "或新建一个。", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutMigrate": "更新此连接器,", + "xpack.stackConnectors.components.serviceNow.deprecatedCalloutTitle": "此连接器类型已过时", + "xpack.stackConnectors.components.serviceNow.descriptionTextAreaFieldLabel": "描述", + "xpack.stackConnectors.components.serviceNow.eventClassTextAreaFieldLabel": "源实例", + "xpack.stackConnectors.components.serviceNow.fetchErrorMsg": "无法提取。检查 ServiceNow 实例的 URL 或 CORS 配置。", + "xpack.stackConnectors.components.serviceNow.impactSelectFieldLabel": "影响", + "xpack.stackConnectors.components.serviceNow.installationCalloutTitle": "要使用此连接器,请先从 ServiceNow 应用商店安装 Elastic 应用。", + "xpack.stackConnectors.components.serviceNow.invalidApiUrlTextField": "URL 无效。", + "xpack.stackConnectors.components.serviceNow.keyIdTextFieldLabel": "JWT Verifier 密钥 ID", + "xpack.stackConnectors.components.serviceNow.messageKeyTextAreaFieldLabel": "消息密钥", + "xpack.stackConnectors.components.serviceNow.metricNameTextAreaFieldLabel": "指标名称", + "xpack.stackConnectors.components.serviceNow.nodeTextAreaFieldLabel": "节点", + "xpack.stackConnectors.components.serviceNow.passwordTextFieldLabel": "密码", + "xpack.stackConnectors.components.serviceNow.prioritySelectFieldLabel": "优先级", + "xpack.stackConnectors.components.serviceNow.privateKeyPassLabelHelpText": "仅在设置了私钥密码时才需要此项", + "xpack.stackConnectors.components.serviceNow.privateKeyPassTextFieldLabel": "私钥密码", + "xpack.stackConnectors.components.serviceNow.privateKeyTextFieldLabel": "私钥", + "xpack.stackConnectors.components.serviceNow.requiredClientIdTextField": "“客户端 ID”必填。", + "xpack.stackConnectors.components.serviceNow.requiredKeyIdTextField": "JWT Verifier 密钥 ID 必填。", + "xpack.stackConnectors.components.serviceNow.requiredPrivateKeyTextField": "“私钥”必填。", + "xpack.stackConnectors.components.serviceNow.requiredSeverityTextField": "“严重性”必填。", + "xpack.stackConnectors.components.serviceNow.requiredUserIdentifierTextField": "“用户标识符”必填。", + "xpack.stackConnectors.components.serviceNow.requiredUsernameTextField": "“用户名”必填。", + "xpack.stackConnectors.components.serviceNow.resourceTextAreaFieldLabel": "资源", + "xpack.stackConnectors.components.serviceNow.setupDevInstance": "设置开发者实例", + "xpack.stackConnectors.components.serviceNow.severityRequiredSelectFieldLabel": "严重性(必需)", + "xpack.stackConnectors.components.serviceNow.severitySelectFieldLabel": "严重性", + "xpack.stackConnectors.components.serviceNow.snInstanceLabel": "ServiceNow 实例", + "xpack.stackConnectors.components.serviceNow.sourceTextAreaFieldLabel": "源", + "xpack.stackConnectors.components.serviceNow.subcategoryTitle": "子类别", + "xpack.stackConnectors.components.serviceNow.title": "事件", + "xpack.stackConnectors.components.serviceNow.titleFieldLabel": "简短描述(必填)", + "xpack.stackConnectors.components.serviceNow.typeTextAreaFieldLabel": "类型", + "xpack.stackConnectors.components.serviceNow.unableToGetChoicesMessage": "无法获取选项", + "xpack.stackConnectors.components.serviceNow.unknown": "未知", + "xpack.stackConnectors.components.serviceNow.updateCalloutText": "连接器已更新。", + "xpack.stackConnectors.components.serviceNow.updateFormCredentialsTitle": "提供身份验证凭据", + "xpack.stackConnectors.components.serviceNow.updateFormInstallTitle": "安装 Elastic ServiceNow 应用", + "xpack.stackConnectors.components.serviceNow.updateFormTitle": "更新 ServiceNow 连接器", + "xpack.stackConnectors.components.serviceNow.updateFormUrlTitle": "输入 ServiceNow 实例 URL", + "xpack.stackConnectors.components.serviceNow.urgencySelectFieldLabel": "紧急性", + "xpack.stackConnectors.components.serviceNow.useOAuth": "使用 OAuth 身份验证", + "xpack.stackConnectors.components.serviceNow.userEmailTextFieldLabel": "用户标识符", + "xpack.stackConnectors.components.serviceNow.usernameTextFieldLabel": "用户名", + "xpack.stackConnectors.components.serviceNow.visitSNStore": "访问 ServiceNow 应用商店", + "xpack.stackConnectors.components.serviceNow.warningMessage": "这会更新此连接器的所有实例并且无法恢复。", + "xpack.stackConnectors.components.serviceNow.correlationIDHelpLabel": "用于更新事件的标识符", + "xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle": "ServiceNow ITOM", + "xpack.stackConnectors.components.serviceNowITOM.event": "事件", + "xpack.stackConnectors.components.serviceNowITOM.selectMessageText": "在 ServiceNow ITOM 中创建事件。", + "xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle": "ServiceNow ITSM", + "xpack.stackConnectors.components.serviceNowITSM.selectMessageText": "在 ServiceNow ITSM 中创建事件。", + "xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle": "ServiceNow SecOps", + "xpack.stackConnectors.components.serviceNowSIR.selectMessageText": "在 ServiceNow SecOps 中创建事件。", + "xpack.stackConnectors.components.serviceNowSIR.title": "安全事件", + "xpack.stackConnectors.components.serviceNowSIR.correlationIDHelpLabel": "用于更新事件的标识符", + "xpack.stackConnectors.components.slack.connectorTypeTitle": "发送到 Slack", + "xpack.stackConnectors.components.slack.error.invalidWebhookUrlText": "Webhook URL 无效。", + "xpack.stackConnectors.components.slack.messageTextAreaFieldLabel": "消息", + "xpack.stackConnectors.components.slack.selectMessageText": "向 Slack 频道或用户发送消息。", + "xpack.stackConnectors.components.slack.webhookUrlHelpLabel": "创建 Slack webhook URL", + "xpack.stackConnectors.components.slack.webhookUrlTextFieldLabel": "Webhook URL", + "xpack.stackConnectors.components.swimlane.unableToGetApplicationFieldsMessage": "无法获取应用程序字段", + "xpack.stackConnectors.components.swimlane.connectorTypeTitle": "创建泳道记录", + "xpack.stackConnectors.components.swimlane.alertIdFieldLabel": "告警 ID", + "xpack.stackConnectors.components.swimlane.apiTokenNameHelpLabel": "提供泳道 API 令牌", + "xpack.stackConnectors.components.swimlane.apiTokenTextFieldLabel": "API 令牌", + "xpack.stackConnectors.components.swimlane.apiUrlTextFieldLabel": "API URL", + "xpack.stackConnectors.components.swimlane.appIdTextFieldLabel": "应用程序 ID", + "xpack.stackConnectors.components.swimlane.caseIdFieldLabel": "案例 ID", + "xpack.stackConnectors.components.swimlane.caseNameFieldLabel": "案例名称", + "xpack.stackConnectors.components.swimlane.commentsFieldLabel": "注释", + "xpack.stackConnectors.components.swimlane.configureConnectionLabel": "配置 API 连接", + "xpack.stackConnectors.components.swimlane.connectorType": "连接器类型", + "xpack.stackConnectors.components.swimlane.descriptionFieldLabel": "描述", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningDesc": "无法选择此连接器,因为其缺失所需的告警字段映射。您可以编辑此连接器以添加所需的字段映射或选择告警类型的连接器。", + "xpack.stackConnectors.components.swimlane.emptyMappingWarningTitle": "此连接器缺失字段映射", + "xpack.stackConnectors.components.swimlane.error.requiredAlertID": "“告警 ID”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredAppIdText": "“应用 ID”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredCaseID": "“案例 ID”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredCaseName": "“案例名称”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredComments": "“注释”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredDescription": "“描述”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredRuleName": "“规则名称”必填。", + "xpack.stackConnectors.components.swimlane.error.requiredSeverity": "“严重性”必填。", + "xpack.stackConnectors.components.swimlane.invalidApiUrlTextField": "URL 无效。", + "xpack.stackConnectors.components.swimlane.mappingTitleTextFieldLabel": "配置字段映射", + "xpack.stackConnectors.components.swimlane.nextStep": "下一步", + "xpack.stackConnectors.components.swimlane.nextStepHelpText": "如果未配置字段映射,泳道连接器类型将设置为 all。", + "xpack.stackConnectors.components.swimlane.prevStep": "返回", + "xpack.stackConnectors.components.swimlane.ruleNameFieldLabel": "规则名称", + "xpack.stackConnectors.components.swimlane.selectMessageText": "在泳道中创建记录", + "xpack.stackConnectors.components.swimlane.severityFieldLabel": "严重性", + "xpack.stackConnectors.components.teams.connectorTypeTitle": "向 Microsoft Teams 频道发送消息。", + "xpack.stackConnectors.components.teams.error.invalidWebhookUrlText": "Webhook URL 无效。", + "xpack.stackConnectors.components.teams.error.requiredMessageText": "“消息”必填。", + "xpack.stackConnectors.components.teams.error.webhookUrlTextLabel": "Webhook URL", + "xpack.stackConnectors.components.teams.messageTextAreaFieldLabel": "消息", + "xpack.stackConnectors.components.teams.selectMessageText": "向 Microsoft Teams 频道发送消息。", + "xpack.stackConnectors.components.teams.webhookUrlHelpLabel": "创建 Microsoft Teams Webhook URL", + "xpack.stackConnectors.components.webhook.connectorTypeTitle": "Webhook 数据", + "xpack.stackConnectors.components.webhook.addHeaderButtonLabel": "添加标头", + "xpack.stackConnectors.components.webhook.authenticationLabel": "身份验证", + "xpack.stackConnectors.components.webhook.bodyCodeEditorAriaLabel": "代码编辑器", + "xpack.stackConnectors.components.webhook.bodyFieldLabel": "正文", + "xpack.stackConnectors.components.webhook.error.invalidUrlTextField": "URL 无效。", + "xpack.stackConnectors.components.webhook.hasAuthSwitchLabel": "此 Webhook 需要身份验证", + "xpack.stackConnectors.components.webhook.headerKeyTextFieldLabel": "钥匙", + "xpack.stackConnectors.components.webhook.headerValueTextFieldLabel": "值", + "xpack.stackConnectors.components.webhook.methodTextFieldLabel": "方法", + "xpack.stackConnectors.components.webhook.passwordTextFieldLabel": "密码", + "xpack.stackConnectors.components.webhook.removeHeaderIconLabel": "钥匙", + "xpack.stackConnectors.components.webhook.selectMessageText": "将请求发送到 Web 服务。", + "xpack.stackConnectors.components.webhook.urlTextFieldLabel": "URL", + "xpack.stackConnectors.components.webhook.userTextFieldLabel": "用户名", + "xpack.stackConnectors.components.webhook.viewHeadersSwitch": "添加 HTTP 标头", + "xpack.stackConnectors.components.xmatters.connectorTypeTitle": "xMatters 数据", + "xpack.stackConnectors.components.xmatters.authenticationLabel": "身份验证", + "xpack.stackConnectors.components.xmatters.basicAuthButtonGroupLegend": "基本身份验证", + "xpack.stackConnectors.components.xmatters.basicAuthLabel": "基本身份验证", + "xpack.stackConnectors.components.xmatters.connectorSettingsLabel": "选择在设置 xMatters 触发器时使用的身份验证方法。", + "xpack.stackConnectors.components.xmatters.error.invalidUrlTextField": "URL 无效。", + "xpack.stackConnectors.components.xmatters.error.invalidUsernameTextField": "用户名无效。", + "xpack.stackConnectors.components.xmatters.error.requiredUrlText": "“URL”必填。", + "xpack.stackConnectors.components.xmatters.initiationUrlHelpText": "包括完整 xMatters url。", + "xpack.stackConnectors.components.xmatters.passwordTextFieldLabel": "密码", + "xpack.stackConnectors.components.xmatters.selectMessageText": "触发 xMatters 工作流。", + "xpack.stackConnectors.components.xmatters.severity": "严重性", + "xpack.stackConnectors.components.xmatters.severitySelectCriticalOptionLabel": "紧急", + "xpack.stackConnectors.components.xmatters.severitySelectHighOptionLabel": "高", + "xpack.stackConnectors.components.xmatters.severitySelectLowOptionLabel": "低", + "xpack.stackConnectors.components.xmatters.severitySelectMediumOptionLabel": "中", + "xpack.stackConnectors.components.xmatters.severitySelectMinimalOptionLabel": "最小", + "xpack.stackConnectors.components.xmatters.tags": "标签", + "xpack.stackConnectors.components.xmatters.urlAuthLabel": "URL 身份验证", + "xpack.stackConnectors.components.xmatters.urlLabel": "发起 URL", + "xpack.stackConnectors.components.xmatters.userCredsLabel": "用户凭据", + "xpack.stackConnectors.components.xmatters.userTextFieldLabel": "用户名", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningDesc": "为连接器配置“创建注释 URL”和“创建注释对象”字段以在外部共享注释。", + "xpack.stackConnectors.components.casesWebhook.createCommentWarningTitle": "无法共享案例注释", + "xpack.stackConnectors.components.serviceNow.deprecatedTooltipTitle": "过时连接器", + "xpack.stackConnectors.components.casesWebhook.error.requiredAuthUserNameText": "“用户名”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateCommentMethodText": "“创建注释方法”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateIncidentResponseKeyText": "“创建案例响应案例 ID 键”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredCreateMethodText": "“创建案例方法”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseCreatedKeyText": "“获取案例响应创建日期键”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseExternalTitleKeyText": "“获取案例响应外部案例标题键”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentResponseUpdatedKeyText": "“获取案例响应更新日期键”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredGetIncidentViewUrlKeyText": "“查看案例 URL”必填。", + "xpack.stackConnectors.components.casesWebhook.error.requiredUpdateMethodText": "“更新案例方法”必填。", + "xpack.stackConnectors.components.webhook.error.requiredAuthUserNameText": "“用户名”必填。", + "xpack.stackConnectors.components.webhook.error.requiredMethodText": "“方法”必填", + "xpack.stackConnectors.components.email.addBccButton": "密送", + "xpack.stackConnectors.components.email.addCcButton": "抄送", + "xpack.stackConnectors.components.email.authenticationLabel": "身份验证", + "xpack.stackConnectors.components.email.clientIdFieldLabel": "客户端 ID", + "xpack.stackConnectors.components.email.clientSecretTextFieldLabel": "客户端密钥", + "xpack.stackConnectors.components.email.fromTextFieldLabel": "发送者", + "xpack.stackConnectors.components.email.hasAuthSwitchLabel": "需要对此服务器进行身份验证", + "xpack.stackConnectors.components.email.hostTextFieldLabel": "主机", + "xpack.stackConnectors.components.email.messageTextAreaFieldLabel": "消息", + "xpack.stackConnectors.components.email.passwordFieldLabel": "密码", + "xpack.stackConnectors.components.email.portTextFieldLabel": "端口", + "xpack.stackConnectors.components.email.recipientBccTextFieldLabel": "密送", + "xpack.stackConnectors.components.email.recipientCopyTextFieldLabel": "抄送", + "xpack.stackConnectors.components.email.recipientTextFieldLabel": "至", + "xpack.stackConnectors.components.email.secureSwitchLabel": "安全", + "xpack.stackConnectors.components.email.serviceTextFieldLabel": "服务", + "xpack.stackConnectors.components.email.subjectTextFieldLabel": "主题", + "xpack.stackConnectors.components.email.tenantIdFieldLabel": "租户 ID", + "xpack.stackConnectors.components.email.userTextFieldLabel": "用户名", + "xpack.stackConnectors.components.index.resetDefaultIndexLabel": "重置默认索引", "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldCandidates": "已识别 {fieldCandidatesCount, plural, other {# 个字段候选项}}。", "xpack.aiops.explainLogRateSpikes.loadingState.identifiedFieldValuePairs": "已识别 {fieldValuePairsCount, plural, other {# 个重要的字段/值对}}。", "xpack.aiops.index.dataLoader.internalServerErrorMessage": "加载索引 {index} 中的数据时出错。{message}。请求可能已超时。请尝试使用较小的样例大小或缩小时间范围。", @@ -31837,20 +32214,7 @@ "xpack.triggersActionsUI.actionVariables.legacyTagsLabel": "其已弃用,将由 {variable} 替代。", "xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "此连接器需要{minimumLicenseRequired}许可证。", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "此规则类型需要{minimumLicenseRequired}许可证。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.missingVariables": "缺少所需{variableCount, plural, other {变量}}:{variables}", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "告警历史记录索引必须以“{alertHistoryPrefix}”开头。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidEmail": "电子邮件地址 {email} 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.notAllowed": "不允许使用电子邮件地址 {email}。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexHelpText": "文档已索引到 {alertHistoryIndex} 索引中。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueMessage": "无法获取 ID 为 {id} 的问题", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "未导入敏感信息。请为以下字段{encryptedFieldsLength, plural, other {}} {secretFieldsLabel}输入值{encryptedFieldsLength, plural, other {}}。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp": "时间戳必须是有效的日期,例如 {nowShortFormat} 或 {nowLongFormat}。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "尝试获取应用程序信息时收到的状态:{status}", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateSuccessToastTitle": "{connectorName} 连接器已更新", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.apiUrlHelpLabel": "提供所需 ServiceNow 实例的完整 URL。如果没有,{instance}。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.serviceNowAppRunning": "在运行更新之前,必须从 ServiceNow 应用商店安装 Elastic 应用。{visitLink} 以安装该应用", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "无法获取 ID 为 {id} 的应用程序", "xpack.triggersActionsUI.components.buttonGroupField.error.requiredField": "{label} 必填。", "xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "无法删除 {numErrors, number} 个{numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}", "xpack.triggersActionsUI.components.passwordField.error.requiredNameText": "{label} 必填。", @@ -32006,340 +32370,6 @@ "xpack.triggersActionsUI.components.addMessageVariables.addRuleVariableTitle": "添加规则变量", "xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton": "添加变量", "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "搜索告警时发生错误", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.commentsTextAreaFieldLabel": "其他注释", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.descriptionTextAreaFieldLabel": "描述", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.tagsFieldLabel": "标签", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhook.titleFieldLabel": "摘要(必填)", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.actionTypeTitle": "Webhook - 案例管理数据", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addHeaderButton": "添加", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addVariable": "添加变量", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseCommentDesc": "Kibana 案例注释", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseDescriptionDesc": "Kibana 案例描述", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTagsDesc": "Kibana 案例标签", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTitleDesc": "Kibana 案例标题", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonHelp": "用于创建注释的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonTextFieldLabel": "创建注释对象", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentMethodTextFieldLabel": "创建注释方法", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlHelp": "用于添加注释到案例的 API URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlTextFieldLabel": "创建注释 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonHelpText": "用于创建案例的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonTextFieldLabel": "创建案例对象", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentMethodTextFieldLabel": "创建案例方法", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyHelpText": "创建案例响应中包含外部案例 ID 的 JSON 键", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyTextFieldLabel": "创建案例响应案例键", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentUrlTextFieldLabel": "创建案例 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.deleteHeaderButton": "删除", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.docLink": "正在配置 Webhook - 案例管理连接器。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentIncidentText": "创建注释对象必须为有效 JSON。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentUrlText": "创建注释 URL 必须为 URL 格式。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateIncidentText": "“创建案例对象”必填并且必须为有效 JSON。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateUrlText": "“创建案例 URL”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredGetIncidentUrlText": "“获取案例 URL”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateIncidentText": "“更新案例对象”必填并且必须为有效 JSON。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateUrlText": "“更新案例 URL”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalIdDesc": "外部系统 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalTitleDesc": "外部系统标题", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyHelp": "获取案例响应中包含外部案例标题的 JSON 键", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyTextFieldLabel": "获取案例响应外部标题键", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlHelp": "用于从外部系统中获取案例详情 JSON 的 API URL。使用变量选择器添加外部系统 ID 到 URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlTextFieldLabel": "获取案例 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.hasAuthSwitchLabel": "此 Webhook 需要身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.httpHeadersTitle": "在用的标头", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonCodeEditorAriaLabel": "代码编辑器", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonFieldLabel": "JSON", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.keyTextFieldLabel": "钥匙", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.next": "下一步", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.passwordTextFieldLabel": "密码", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.previous": "上一步", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.selectMessageText": "发送请求到案例管理 Web 服务。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step1": "设置连接器", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2": "创建案例", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2Description": "设置字段以在外部系统中创建案例。查阅您服务的 API 文档以了解需要哪些字段", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3": "获取案例信息", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3Description": "设置字段以在外部系统中添加案例注释。对于某些系统,这可能采取与在案例中创建更新相同的方法。查阅您服务的 API 文档以了解需要哪些字段。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4": "注释和更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4a": "在案例中创建更新", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4aDescription": "设置字段以在外部系统中创建案例更新。对于某些系统,这可能采取与添加案例注释相同的方法。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4b": "在案例中添加注释", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4bDescription": "设置字段以在外部系统中添加案例注释。对于某些系统,这可能采取与在案例中创建更新相同的方法。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonHelpl": "用于更新案例的 JSON 对象。使用变量选择器将案例数据添加到有效负载。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonTextFieldLabel": "更新案例对象", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentMethodTextFieldLabel": "更新案例方法", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlHelp": "用于更新案例的 API URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlTextFieldLabel": "更新案例 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.userTextFieldLabel": "用户名", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.valueTextFieldLabel": "值", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewHeadersSwitch": "添加 HTTP 标头", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlHelp": "用于查看外部系统中的案例的 URL。使用变量选择器添加外部系统 ID 或外部系统标题到 URL。", - "xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlTextFieldLabel": "外部案例查看 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.common.requiredShortDescTextField": "“简短描述”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientIdHelpLabel": "配置客户端 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.clientSecretHelpLabel": "配置客户端密钥", - "xpack.triggersActionsUI.components.builtinActionTypes.email.exchangeForm.tenantIdHelpLabel": "配置租户 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "发送到电子邮件", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.amazonSesServerTypeLabel": "Amazon SES", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel": "配置电子邮件帐户", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.elasticCloudServerTypeLabel": "Elastic Cloud", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.exchangeServerTypeLabel": "MS Exchange Server", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "其他", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "从您的服务器发送电子邮件。", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.updateErrorNotificationText": "无法获取服务配置", - "xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "告警历史记录索引必须包含有效的后缀。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.invalidPortText": "端口无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "“用户名”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "“客户端 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "“文档”必填,并且应为有效的 JSON 对象。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "未输入收件人、抄送、密送。 至少需要输入一个。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "“发送者”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "“主机”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "“消息”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "“端口”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "“消息”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "“服务”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "“消息”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "“主题”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "“租户 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "“正文”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookSummaryText": "“标题”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "索引数据", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "选择……", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.configureIndexHelpLabel": "配置索引连接器。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.connectorSectionTitle": "写入到索引", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "将此时间字段设置为索引文档的时间。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "为每个文档定义时间字段", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "要索引的文档", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.notValidIndexText": "索引无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "时间字段", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "索引文档示例。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "基于您的数据视图", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesToQueryLabel": "索引", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel": "代码编辑器", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndex": "Elasticsearch 索引", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.preconfiguredIndexDocLink": "查看文档。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshLabel": "刷新索引", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshTooltip": "刷新影响的分片以使此操作对搜索可见。", - "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText": "将数据索引到 Elasticsearch 中。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "API 令牌", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "其他注释", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "描述", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "电子邮件地址", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "标签", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "标签不能包含空格。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "父问题", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "项目键", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "“摘要”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "键入内容进行搜索", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "键入内容进行搜索", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "正在加载……", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.selectMessageText": "在 Jira 创建事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.severitySelectFieldLabel": "优先级", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.summaryFieldLabel": "摘要(必填)", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetFieldsMessage": "无法获取字段", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "无法获取问题", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "无法获取问题类型", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "问题类型", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "发送到 PagerDuty", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlInvalid": "API URL 无效", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "API URL(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "类(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.componentTextFieldLabel": "组件(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextFieldLabel": "DedupKey(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.dedupKeyTextRequiredFieldLabel": "DedupKey", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredDedupKeyText": "解决或确认事件时需要 DedupKey。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredRoutingKeyText": "需要集成密钥/路由密钥。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredSummaryText": "“摘要”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventActionSelectFieldLabel": "事件操作", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectAcknowledgeOptionLabel": "确认", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "解决", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "触发", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "组(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "配置 PagerDuty 帐户", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "集成密钥", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "在 PagerDuty 中发送事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectCriticalOptionLabel": "紧急", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectErrorOptionLabel": "错误", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectFieldLabel": "严重性(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectInfoOptionLabel": "信息", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectWarningOptionLabel": "警告", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "源(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "摘要", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "时间戳(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Resilient", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "API 密钥 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "API 密钥密码", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "其他注释", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "描述", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "名称(必填)", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "组织 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "“名称”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "在 IBM Resilient 中创建事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "严重性", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "无法获取事件类型", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetSeverityMessage": "无法获取严重性", - "xpack.triggersActionsUI.components.builtinActionTypes.resilient.urgencySelectFieldLabel": "事件类型", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle": "发送到服务器日志", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logLevelFieldLabel": "级别", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "消息", - "xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "将消息添加到 Kibana 日志。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "ServiceNow 实例 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "未安装 Elastic ServiceNow 应用", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.content": "请前往 ServiceNow 应用商店并安装该应用程序", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout.errorMessage": "错误消息", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.cancelButtonText": "取消", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.categoryTitle": "类别", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientIdTextFieldLabel": "客户端 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.clientSecretTextFieldLabel": "客户端密钥", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.commentsTextAreaFieldLabel": "其他注释", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.confirmButtonText": "更新", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationDisplay": "相关性显示(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.correlationID": "相关性 ID(可选)", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutCreate": "或新建一个。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate": "更新此连接器,", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutTitle": "此连接器类型已过时", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "描述", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "源实例", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.fetchErrorMsg": "无法提取。检查 ServiceNow 实例的 URL 或 CORS 配置。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "影响", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "要使用此连接器,请先从 ServiceNow 应用商店安装 Elastic 应用。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.keyIdTextFieldLabel": "JWT Verifier 密钥 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "消息密钥", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "指标名称", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "节点", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "密码", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "优先级", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassLabelHelpText": "仅在设置了私钥密码时才需要此项", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyPassTextFieldLabel": "私钥密码", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.privateKeyTextFieldLabel": "私钥", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredClientIdTextField": "“客户端 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField": "JWT Verifier 密钥 ID 必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPrivateKeyTextField": "“私钥”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "“严重性”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserIdentifierTextField": "“用户标识符”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "“用户名”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "资源", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "设置开发者实例", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "严重性(必需)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severitySelectFieldLabel": "严重性", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.snInstanceLabel": "ServiceNow 实例", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.sourceTextAreaFieldLabel": "源", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.subcategoryTitle": "子类别", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.title": "事件", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.titleFieldLabel": "简短描述(必填)", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.typeTextAreaFieldLabel": "类型", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unableToGetChoicesMessage": "无法获取选项", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.unknown": "未知", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.updateCalloutText": "连接器已更新。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormCredentialsTitle": "提供身份验证凭据", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormInstallTitle": "安装 Elastic ServiceNow 应用", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle": "更新 ServiceNow 连接器", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormUrlTitle": "输入 ServiceNow 实例 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.urgencySelectFieldLabel": "紧急性", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.useOAuth": "使用 OAuth 身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.userEmailTextFieldLabel": "用户标识符", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.usernameTextFieldLabel": "用户名", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.visitSNStore": "访问 ServiceNow 应用商店", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.warningMessage": "这会更新此连接器的所有实例并且无法恢复。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowAction.correlationIDHelpLabel": "用于更新事件的标识符", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.actionTypeTitle": "ServiceNow ITOM", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowITOM.event": "事件", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.selectMessageText": "在 ServiceNow ITOM 中创建事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.actionTypeTitle": "ServiceNow ITSM", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITSM.selectMessageText": "在 ServiceNow ITSM 中创建事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.actionTypeTitle": "ServiceNow SecOps", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIR.selectMessageText": "在 ServiceNow SecOps 中创建事件。", - "xpack.triggersActionsUI.components.builtinActionTypes.servicenowSIR.title": "安全事件", - "xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "用于更新事件的标识符", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "发送到 Slack", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "Webhook URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "消息", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "向 Slack 频道或用户发送消息。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "创建 Slack webhook URL", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Webhook URL", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationFieldsMessage": "无法获取应用程序字段", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "创建泳道记录", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "告警 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "提供泳道 API 令牌", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "API 令牌", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "API URL", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.appIdTextFieldLabel": "应用程序 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseIdFieldLabel": "案例 ID", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.caseNameFieldLabel": "案例名称", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.commentsFieldLabel": "注释", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.configureConnectionLabel": "配置 API 连接", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.connectorType": "连接器类型", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.descriptionFieldLabel": "描述", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "无法选择此连接器,因为其缺失所需的告警字段映射。您可以编辑此连接器以添加所需的字段映射或选择告警类型的连接器。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "此连接器缺失字段映射", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "“告警 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "“应用 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "“案例 ID”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "“案例名称”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "“注释”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "“描述”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "“规则名称”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "“严重性”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "配置字段映射", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "下一步", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "如果未配置字段映射,泳道连接器类型将设置为 all。", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "返回", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "规则名称", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "在泳道中创建记录", - "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "严重性", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "向 Microsoft Teams 频道发送消息。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "Webhook URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "“消息”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.webhookUrlTextLabel": "Webhook URL", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "消息", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "向 Microsoft Teams 频道发送消息。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "创建 Microsoft Teams Webhook URL", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Webhook 数据", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButtonLabel": "添加标头", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "代码编辑器", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "正文", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "此 Webhook 需要身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerKeyTextFieldLabel": "钥匙", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerValueTextFieldLabel": "值", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "方法", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "密码", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.removeHeaderIconLabel": "钥匙", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "将请求发送到 Web 服务。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "用户名", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "添加 HTTP 标头", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "xMatters 数据", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthButtonGroupLegend": "基本身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "基本身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "选择在设置 xMatters 触发器时使用的身份验证方法。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "URL 无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUsernameTextField": "用户名无效。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "“URL”必填。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "包括完整 xMatters url。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "密码", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "触发 xMatters 工作流。", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "严重性", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "紧急", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectHighOptionLabel": "高", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectLowOptionLabel": "低", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMediumOptionLabel": "中", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectMinimalOptionLabel": "最小", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.tags": "标签", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlAuthLabel": "URL 身份验证", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlLabel": "发起 URL", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userCredsLabel": "用户凭据", - "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userTextFieldLabel": "用户名", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorButtonLabel": "创建连接器", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyBody": "配置电子邮件、Slack、Elasticsearch,以及 Kibana 运行的第三方服务。", "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addConnectorEmptyTitle": "创建您的首个连接器", @@ -32361,8 +32391,6 @@ "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorMessage": "找不到编辑器,请刷新页面并重试", "xpack.triggersActionsUI.components.jsonEditorWithMessageVariable.noEditorErrorTitle": "无法添加消息变量", "xpack.triggersActionsUI.components.simpleConnectorForm.secrets.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningDesc": "为连接器配置“创建注释 URL”和“创建注释对象”字段以在外部共享注释。", - "xpack.triggersActionsUI.components.textAreaWithMessageVariable.createCommentWarningTitle": "无法共享案例注释", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "连接器", "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]:晚于 [dateEnd]", "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定", @@ -32435,7 +32463,6 @@ "xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "正在加载连接器设置……", "xpack.triggersActionsUI.sections.actionForm.actionSectionsTitle": "操作", "xpack.triggersActionsUI.sections.actionForm.addActionButtonLabel": "添加操作", - "xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle": "过时连接器", "xpack.triggersActionsUI.sections.actionForm.getMoreConnectorsTitle": "获取更多连接器", "xpack.triggersActionsUI.sections.actionForm.getMoreRuleTypesTitle": "获取更多规则类型", "xpack.triggersActionsUI.sections.actionForm.incidentManagementSystemLabel": "事件管理系统", @@ -32474,17 +32501,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.actionErrorToolTip": "操作包含错误。", "xpack.triggersActionsUI.sections.actionTypeForm.actionRunWhenInActionGroup": "运行条件", "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "添加连接器", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredAuthUserNameText": "“用户名”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateCommentMethodText": "“创建注释方法”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateIncidentResponseKeyText": "“创建案例响应案例 ID 键”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateMethodText": "“创建案例方法”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseCreatedKeyText": "“获取案例响应创建日期键”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseExternalTitleKeyText": "“获取案例响应外部案例标题键”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseUpdatedKeyText": "“获取案例响应更新日期键”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentViewUrlKeyText": "“查看案例 URL”必填。", - "xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredUpdateMethodText": "“更新案例方法”必填。", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "“用户名”必填。", - "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "“方法”必填", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "选择连接器", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”", "xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "取消", @@ -32494,25 +32510,6 @@ "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason": "原因", "xpack.triggersActionsUI.sections.alertsTable.column.actions": "操作", "xpack.triggersActionsUI.sections.alertsTable.leadingControl.viewDetails": "查看详情", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "密送", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "抄送", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "身份验证", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientIdFieldLabel": "客户端 ID", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientSecretTextFieldLabel": "客户端密钥", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel": "发送者", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel": "需要对此服务器进行身份验证", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel": "主机", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.messageTextAreaFieldLabel": "消息", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel": "密码", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel": "端口", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel": "密送", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel": "抄送", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel": "至", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.secureSwitchLabel": "安全", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.serviceTextFieldLabel": "服务", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel": "主题", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.tenantIdFieldLabel": "租户 ID", - "xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel": "用户名", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseCancelButtonText": "取消", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseConfirmButtonText": "放弃更改", "xpack.triggersActionsUI.sections.confirmRuleClose.confirmRuleCloseMessage": "您无法恢复未保存更改。", @@ -32702,7 +32699,6 @@ "xpack.triggersActionsUI.sections.rulesList.removeAllButton": "全部删除", "xpack.triggersActionsUI.sections.rulesList.removeCancelButton": "取消", "xpack.triggersActionsUI.sections.rulesList.removeConfirmButton": "全部删除", - "xpack.triggersActionsUI.sections.rulesList.resetDefaultIndexLabel": "重置默认索引", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDecrypting": "解密规则时发生错误。", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonDisabled": "无法执行规则,因为在规则禁用之后已运行。", "xpack.triggersActionsUI.sections.rulesList.ruleErrorReasonLicense": "无法运行规则", diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 999b7b00a13a..0643a7d266a8 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -917,13 +917,13 @@ export function getActionType(): ActionTypeModel { id: '.email', iconClass: 'email', selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText', + 'xpack.stackConnectors.components.email.selectMessageText', { defaultMessage: 'Send email from your server.', } ), actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle', + 'xpack.stackConnectors.components.email.connectorTypeTitle', { defaultMessage: 'Send to email', } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/translations.ts deleted file mode 100644 index 785840477af6..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/translations.ts +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const CREATE_URL_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateUrlText', - { - defaultMessage: 'Create case URL is required.', - } -); -export const CREATE_INCIDENT_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateIncidentText', - { - defaultMessage: 'Create case object is required and must be valid JSON.', - } -); - -export const CREATE_METHOD_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateMethodText', - { - defaultMessage: 'Create case method is required.', - } -); - -export const CREATE_RESPONSE_KEY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateIncidentResponseKeyText', - { - defaultMessage: 'Create case response case id key is required.', - } -); - -export const UPDATE_URL_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateUrlText', - { - defaultMessage: 'Update case URL is required.', - } -); -export const UPDATE_INCIDENT_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredUpdateIncidentText', - { - defaultMessage: 'Update case object is required and must be valid JSON.', - } -); - -export const UPDATE_METHOD_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredUpdateMethodText', - { - defaultMessage: 'Update case method is required.', - } -); - -export const CREATE_COMMENT_URL_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentUrlText', - { - defaultMessage: 'Create comment URL must be URL format.', - } -); -export const CREATE_COMMENT_MESSAGE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredCreateCommentIncidentText', - { - defaultMessage: 'Create comment object must be valid JSON.', - } -); - -export const CREATE_COMMENT_METHOD_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredCreateCommentMethodText', - { - defaultMessage: 'Create comment method is required.', - } -); - -export const GET_INCIDENT_URL_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.requiredGetIncidentUrlText', - { - defaultMessage: 'Get case URL is required.', - } -); -export const GET_RESPONSE_EXTERNAL_TITLE_KEY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseExternalTitleKeyText', - { - defaultMessage: 'Get case response external case title key is re quired.', - } -); -export const GET_RESPONSE_EXTERNAL_CREATED_KEY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseCreatedKeyText', - { - defaultMessage: 'Get case response created date key is required.', - } -); -export const GET_RESPONSE_EXTERNAL_UPDATED_KEY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentResponseUpdatedKeyText', - { - defaultMessage: 'Get case response updated date key is required.', - } -); -export const GET_INCIDENT_VIEW_URL_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredGetIncidentViewUrlKeyText', - { - defaultMessage: 'View case URL is required.', - } -); - -export const MISSING_VARIABLES = (variables: string[]) => - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.error.missingVariables', - { - defaultMessage: - 'Missing required {variableCount, plural, one {variable} other {variables}}: {variables}', - values: { variableCount: variables.length, variables: variables.join(', ') }, - } - ); - -export const USERNAME_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.casesWebhookAction.error.requiredAuthUserNameText', - { - defaultMessage: 'Username is required.', - } -); - -export const SUMMARY_REQUIRED = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookSummaryText', - { - defaultMessage: 'Title is required.', - } -); - -export const KEY_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.keyTextFieldLabel', - { - defaultMessage: 'Key', - } -); - -export const VALUE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.valueTextFieldLabel', - { - defaultMessage: 'Value', - } -); - -export const ADD_BUTTON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addHeaderButton', - { - defaultMessage: 'Add', - } -); - -export const DELETE_BUTTON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.deleteHeaderButton', - { - defaultMessage: 'Delete', - description: 'Delete HTTP header', - } -); - -export const CREATE_INCIDENT_METHOD = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentMethodTextFieldLabel', - { - defaultMessage: 'Create Case Method', - } -); - -export const CREATE_INCIDENT_URL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentUrlTextFieldLabel', - { - defaultMessage: 'Create Case URL', - } -); - -export const CREATE_INCIDENT_JSON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonTextFieldLabel', - { - defaultMessage: 'Create Case Object', - } -); - -export const CREATE_INCIDENT_JSON_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentJsonHelpText', - { - defaultMessage: - 'JSON object to create case. Use the variable selector to add Cases data to the payload.', - } -); - -export const JSON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonFieldLabel', - { - defaultMessage: 'JSON', - } -); -export const CODE_EDITOR = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.jsonCodeEditorAriaLabel', - { - defaultMessage: 'Code editor', - } -); - -export const CREATE_INCIDENT_RESPONSE_KEY = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyTextFieldLabel', - { - defaultMessage: 'Create Case Response Case Key', - } -); - -export const CREATE_INCIDENT_RESPONSE_KEY_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createIncidentResponseKeyHelpText', - { - defaultMessage: 'JSON key in create case response that contains the external case id', - } -); - -export const ADD_CASES_VARIABLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.addVariable', - { - defaultMessage: 'Add variable', - } -); - -export const GET_INCIDENT_URL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlTextFieldLabel', - { - defaultMessage: 'Get Case URL', - } -); -export const GET_INCIDENT_URL_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentUrlHelp', - { - defaultMessage: - 'API URL to GET case details JSON from external system. Use the variable selector to add external system id to the url.', - } -); - -export const GET_INCIDENT_TITLE_KEY = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyTextFieldLabel', - { - defaultMessage: 'Get Case Response External Title Key', - } -); -export const GET_INCIDENT_TITLE_KEY_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.getIncidentResponseExternalTitleKeyHelp', - { - defaultMessage: 'JSON key in get case response that contains the external case title', - } -); - -export const EXTERNAL_INCIDENT_VIEW_URL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlTextFieldLabel', - { - defaultMessage: 'External Case View URL', - } -); -export const EXTERNAL_INCIDENT_VIEW_URL_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewIncidentUrlHelp', - { - defaultMessage: - 'URL to view case in external system. Use the variable selector to add external system id or external system title to the url.', - } -); - -export const UPDATE_INCIDENT_METHOD = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentMethodTextFieldLabel', - { - defaultMessage: 'Update Case Method', - } -); - -export const UPDATE_INCIDENT_URL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlTextFieldLabel', - { - defaultMessage: 'Update Case URL', - } -); -export const UPDATE_INCIDENT_URL_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentUrlHelp', - { - defaultMessage: 'API URL to update case.', - } -); - -export const UPDATE_INCIDENT_JSON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonTextFieldLabel', - { - defaultMessage: 'Update Case Object', - } -); -export const UPDATE_INCIDENT_JSON_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.updateIncidentJsonHelpl', - { - defaultMessage: - 'JSON object to update case. Use the variable selector to add Cases data to the payload.', - } -); - -export const CREATE_COMMENT_METHOD = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentMethodTextFieldLabel', - { - defaultMessage: 'Create Comment Method', - } -); -export const CREATE_COMMENT_URL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlTextFieldLabel', - { - defaultMessage: 'Create Comment URL', - } -); - -export const CREATE_COMMENT_URL_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentUrlHelp', - { - defaultMessage: 'API URL to add comment to case.', - } -); - -export const CREATE_COMMENT_JSON = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonTextFieldLabel', - { - defaultMessage: 'Create Comment Object', - } -); -export const CREATE_COMMENT_JSON_HELP = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.createCommentJsonHelp', - { - defaultMessage: - 'JSON object to create a comment. Use the variable selector to add Cases data to the payload.', - } -); - -export const HAS_AUTH = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.hasAuthSwitchLabel', - { - defaultMessage: 'Require authentication for this webhook', - } -); - -export const USERNAME = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.userTextFieldLabel', - { - defaultMessage: 'Username', - } -); - -export const PASSWORD = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.passwordTextFieldLabel', - { - defaultMessage: 'Password', - } -); - -export const HEADERS_SWITCH = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.viewHeadersSwitch', - { - defaultMessage: 'Add HTTP header', - } -); - -export const HEADERS_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.httpHeadersTitle', - { - defaultMessage: 'Headers in use', - } -); - -export const AUTH_TITLE = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.authenticationLabel', - { - defaultMessage: 'Authentication', - } -); - -export const STEP_1 = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step1', - { - defaultMessage: 'Set up connector', - } -); - -export const STEP_2 = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2', - { - defaultMessage: 'Create case', - } -); - -export const STEP_2_DESCRIPTION = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step2Description', - { - defaultMessage: - 'Set fields to create the case in the external system. Check your service’s API documentation to understand what fields are required', - } -); - -export const STEP_3 = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3', - { - defaultMessage: 'Get case information', - } -); - -export const STEP_3_DESCRIPTION = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step3Description', - { - defaultMessage: - 'Set fields to add comments to the case in external system. For some systems, this may be the same method as creating updates in cases. Check your service’s API documentation to understand what fields are required.', - } -); - -export const STEP_4 = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4', - { - defaultMessage: 'Comments and updates', - } -); - -export const STEP_4A = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4a', - { - defaultMessage: 'Create update in case', - } -); - -export const STEP_4A_DESCRIPTION = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4aDescription', - { - defaultMessage: - 'Set fields to create updates to the case in external system. For some systems, this may be the same method as adding comments to cases.', - } -); - -export const STEP_4B = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4b', - { - defaultMessage: 'Add comment in case', - } -); - -export const STEP_4B_DESCRIPTION = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.step4bDescription', - { - defaultMessage: - 'Set fields to add comments to the case in external system. For some systems, this may be the same method as creating updates in cases.', - } -); - -export const NEXT = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.next', - { - defaultMessage: 'Next', - } -); - -export const PREVIOUS = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.previous', - { - defaultMessage: 'Previous', - } -); - -export const CASE_TITLE_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTitleDesc', - { - defaultMessage: 'Kibana case title', - } -); - -export const CASE_DESCRIPTION_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseDescriptionDesc', - { - defaultMessage: 'Kibana case description', - } -); - -export const CASE_TAGS_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseTagsDesc', - { - defaultMessage: 'Kibana case tags', - } -); - -export const CASE_COMMENT_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.caseCommentDesc', - { - defaultMessage: 'Kibana case comment', - } -); - -export const EXTERNAL_ID_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalIdDesc', - { - defaultMessage: 'External system id', - } -); - -export const EXTERNAL_TITLE_DESC = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.externalTitleDesc', - { - defaultMessage: 'External system title', - } -); - -export const DOC_LINK = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.casesWebhookAction.docLink', - { - defaultMessage: 'Configuring Webhook - Case Management connector.', - } -); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.test.tsx deleted file mode 100644 index 45169e0fcb03..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/cases_webhook/webhook.test.tsx +++ /dev/null @@ -1,54 +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 { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.cases-webhook'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('logoWebhook'); - }); -}); - -describe('webhook action params validation', () => { - test('action params validation succeeds when action params is valid', async () => { - const actionParams = { - subActionParams: { incident: { title: 'some title {{test}}' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { 'subActionParams.incident.title': [] }, - }); - }); - - test('params validation fails when body is not valid', async () => { - const actionParams = { - subActionParams: { incident: { title: '' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - 'subActionParams.incident.title': ['Title is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts deleted file mode 100644 index e5e5da50eca0..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts +++ /dev/null @@ -1,58 +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 { ValidatedEmail, ValidateEmailAddressesOptions } from '@kbn/actions-plugin/common'; -import { getServerLogActionType } from './server_log'; -import { getSlackActionType } from './slack'; -import { getEmailActionType } from './email'; -import { getIndexActionType } from './es_index'; -import { getPagerDutyActionType } from './pagerduty'; -import { getSwimlaneActionType } from './swimlane'; -import { getCasesWebhookActionType } from './cases_webhook'; -import { getWebhookActionType } from './webhook'; -import { getXmattersActionType } from './xmatters'; -import { TypeRegistry } from '../../type_registry'; -import { ActionTypeModel } from '../../../types'; -import { - getServiceNowITSMActionType, - getServiceNowSIRActionType, - getServiceNowITOMActionType, -} from './servicenow'; -import { getJiraActionType } from './jira'; -import { getResilientActionType } from './resilient'; -import { getTeamsActionType } from './teams'; - -export interface RegistrationServices { - validateEmailAddresses: ( - addresses: string[], - options?: ValidateEmailAddressesOptions - ) => ValidatedEmail[]; -} - -export function registerBuiltInActionTypes({ - actionTypeRegistry, - services, -}: { - actionTypeRegistry: TypeRegistry; - services: RegistrationServices; -}) { - actionTypeRegistry.register(getServerLogActionType()); - actionTypeRegistry.register(getSlackActionType()); - actionTypeRegistry.register(getEmailActionType(services)); - actionTypeRegistry.register(getIndexActionType()); - actionTypeRegistry.register(getPagerDutyActionType()); - actionTypeRegistry.register(getSwimlaneActionType()); - actionTypeRegistry.register(getCasesWebhookActionType()); - actionTypeRegistry.register(getWebhookActionType()); - actionTypeRegistry.register(getXmattersActionType()); - actionTypeRegistry.register(getServiceNowITSMActionType()); - actionTypeRegistry.register(getServiceNowITOMActionType()); - actionTypeRegistry.register(getServiceNowSIRActionType()); - actionTypeRegistry.register(getJiraActionType()); - actionTypeRegistry.register(getResilientActionType()); - actionTypeRegistry.register(getTeamsActionType()); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts deleted file mode 100644 index 3db3aeaaaa66..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getPagerDutyActionType } from './pagerduty'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts deleted file mode 100644 index 2794a63d4a8b..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getResilientActionType } from './resilient'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx deleted file mode 100644 index c46bcd6a02c7..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.resilient'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - }); -}); - -describe('resilient action params validation', () => { - test('action params validation succeeds when action params is valid', async () => { - const actionParams = { - subActionParams: { incident: { name: 'some title {{test}}' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { 'subActionParams.incident.name': [] }, - }); - }); - - test('params validation fails when body is not valid', async () => { - const actionParams = { - subActionParams: { incident: { name: '' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - 'subActionParams.incident.name': ['Name is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts deleted file mode 100644 index 0edbef22ee9c..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getServerLogActionType } from './server_log'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx deleted file mode 100644 index 098c65f29456..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.server-log'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('logsApp'); - }); -}); - -describe('action params validation', () => { - test('action params validation succeeds when action params is valid', async () => { - const actionParams = { - message: 'test message', - level: 'trace', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { message: [] }, - }); - }); - - test('params validation fails when message is not valid', async () => { - const actionParams = { - message: '', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - message: ['Message is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx deleted file mode 100644 index 9e786a189ec4..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx +++ /dev/null @@ -1,80 +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 { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const SERVICENOW_ITSM_ACTION_TYPE_ID = '.servicenow'; -const SERVICENOW_SIR_ACTION_TYPE_ID = '.servicenow-sir'; -const SERVICENOW_ITOM_ACTION_TYPE_ID = '.servicenow-itom'; -let actionTypeRegistry: TypeRegistry; - -beforeAll(() => { - actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); -}); - -describe('actionTypeRegistry.get() works', () => { - [ - SERVICENOW_ITSM_ACTION_TYPE_ID, - SERVICENOW_SIR_ACTION_TYPE_ID, - SERVICENOW_ITOM_ACTION_TYPE_ID, - ].forEach((id) => { - test(`${id}: action type static data is as expected`, () => { - const actionTypeModel = actionTypeRegistry.get(id); - expect(actionTypeModel.id).toEqual(id); - }); - }); -}); - -describe('servicenow action params validation', () => { - [SERVICENOW_ITSM_ACTION_TYPE_ID, SERVICENOW_SIR_ACTION_TYPE_ID].forEach((id) => { - test(`${id}: action params validation succeeds when action params is valid`, async () => { - const actionTypeModel = actionTypeRegistry.get(id); - const actionParams = { - subActionParams: { incident: { short_description: 'some title {{test}}' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { ['subActionParams.incident.short_description']: [] }, - }); - }); - - test(`${id}: params validation fails when short_description is not valid`, async () => { - const actionTypeModel = actionTypeRegistry.get(id); - const actionParams = { - subActionParams: { incident: { short_description: '' }, comments: [] }, - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - ['subActionParams.incident.short_description']: ['Short description is required.'], - }, - }); - }); - }); - - test(`${SERVICENOW_ITOM_ACTION_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { - const actionTypeModel = actionTypeRegistry.get(SERVICENOW_ITOM_ACTION_TYPE_ID); - const actionParams = { subActionParams: { severity: 'Critical' } }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { ['severity']: [] }, - }); - }); - - test(`${SERVICENOW_ITOM_ACTION_TYPE_ID}: params validation fails when severity is not valid`, async () => { - const actionTypeModel = actionTypeRegistry.get(SERVICENOW_ITOM_ACTION_TYPE_ID); - const actionParams = { subActionParams: { severity: null } }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { ['severity']: ['Severity is required.'] }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx deleted file mode 100644 index 45689fbd5026..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx +++ /dev/null @@ -1,54 +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 { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.slack'; -let actionTypeModel: ActionTypeModel; - -beforeAll(async () => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('logoSlack'); - }); -}); - -describe('slack action params validation', () => { - test('if action params validation succeeds when action params is valid', async () => { - const actionParams = { - message: 'message {test}', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { message: [] }, - }); - }); - - test('params validation fails when message is not valid', async () => { - const actionParams = { - message: '', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - message: ['Message is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/index.ts deleted file mode 100644 index 39a57e1bccb6..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getSwimlaneActionType } from './swimlane'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/index.ts deleted file mode 100644 index 84af06ff53bf..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getTeamsActionType } from './teams'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx deleted file mode 100644 index e08853abf9c1..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.teams'; -let actionTypeModel: ActionTypeModel; - -beforeAll(async () => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - }); -}); - -describe('teams action params validation', () => { - test('if action params validation succeeds when action params is valid', async () => { - const actionParams = { - message: 'message {test}', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { message: [] }, - }); - }); - - test('params validation fails when message is not valid', async () => { - const actionParams = { - message: '', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - message: ['Message is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx deleted file mode 100644 index dfc3aae39586..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx +++ /dev/null @@ -1,54 +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 { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.webhook'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('logoWebhook'); - }); -}); - -describe('webhook action params validation', () => { - test('action params validation succeeds when action params is valid', async () => { - const actionParams = { - body: 'message {test}', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { body: [] }, - }); - }); - - test('params validation fails when body is not valid', async () => { - const actionParams = { - body: '', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - body: ['Body is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/index.ts deleted file mode 100644 index 54bc4fd06acd..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getActionType as getXmattersActionType } from './xmatters'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx deleted file mode 100644 index 980fa90caf4b..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '..'; -import { ActionTypeModel } from '../../../../types'; -import { registrationServicesMock } from '../../../../mocks'; - -const ACTION_TYPE_ID = '.xmatters'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry, services: registrationServicesMock }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.actionTypeTitle).toEqual('xMatters data'); - }); -}); - -describe('xmatters action params validation', () => { - test('action params validation succeeds when action params is valid', async () => { - const actionParams = { - alertActionGroupName: 'Small t-shirt', - signalId: 'c9437cab-6a5b-45e8-bc8a-f4a8af440e97', - ruleName: 'Test xMatters', - date: '2022-01-18T19:01:08.818Z', - severity: 'high', - spaceId: 'default', - tags: 'test1, test2', - }; - - expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { alertActionGroupName: [], signalId: [] }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/index.ts new file mode 100644 index 000000000000..3bc994313093 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { JsonEditorWithMessageVariables } from './json_editor_with_message_variables'; +export { HiddenField } from './hidden_field'; +export { PasswordField } from './password_field'; +export { TextFieldWithMessageVariables } from './text_field_with_message_variables'; +export { TextAreaWithMessageVariables } from './text_area_with_message_variables'; +export { SimpleConnectorForm } from './simple_connector_form'; +export type { ConfigFieldSchema, SecretsFieldSchema } from './simple_connector_form'; +export { ButtonGroupField } from './button_group_field'; +export { JsonFieldWrapper } from './json_field_wrapper'; +export { MustacheTextFieldWrapper } from './mustache_text_field_wrapper'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx index 62745a6eb099..36b3bf2cb153 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { act, render, RenderResult } from '@testing-library/react'; -import { FormTestProvider } from './builtin_action_types/test_utils'; +import { FormTestProvider } from './test_utils'; import { ConfigFieldSchema, SecretsFieldSchema, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/test_utils.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/test_utils.tsx new file mode 100644 index 000000000000..39cff673908b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/test_utils.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { of } from 'rxjs'; +import { I18nProvider } from '@kbn/i18n-react'; +import { EuiButton } from '@elastic/eui'; +import { Form, useForm, FormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; +import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; + +import { ConnectorServices } from '../../types'; +import { TriggersAndActionsUiServices } from '../..'; +import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; +import { ConnectorProvider } from '../context/connector_context'; + +interface FormTestProviderProps { + children: React.ReactNode; + defaultValue?: Record; + onSubmit?: ({ data, isValid }: { data: FormData; isValid: boolean }) => Promise; + connectorServices?: ConnectorServices; +} + +const FormTestProviderComponent: React.FC = ({ + children, + defaultValue, + onSubmit, + connectorServices = { validateEmailAddresses: jest.fn() }, +}) => { + const { form } = useForm({ defaultValue }); + const { submit } = form; + + const onClick = useCallback(async () => { + const res = await submit(); + if (onSubmit) { + onSubmit(res); + } + }, [onSubmit, submit]); + + return ( + + +
{children}
+ +
+
+ ); +}; + +FormTestProviderComponent.displayName = 'FormTestProvider'; +export const FormTestProvider = React.memo(FormTestProviderComponent); + +type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; +export interface AppMockRenderer { + render: UiRender; + coreStart: TriggersAndActionsUiServices; +} + +export const createAppMockRenderer = (): AppMockRenderer => { + const services = createStartServicesMock(); + const theme$ = of({ darkMode: false }); + + const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( + + + {children} + + + ); + AppWrapper.displayName = 'AppWrapper'; + const render: UiRender = (ui, options) => { + return reactRender(ui, { + wrapper: AppWrapper, + ...options, + }); + }; + return { + coreStart: services, + render, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 113d274c7aeb..19d3b038c635 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -12,7 +12,6 @@ export { INTERNAL_BASE_ALERTING_API_PATH, } from '@kbn/alerting-plugin/common'; export { BASE_ACTION_API_PATH, INTERNAL_BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; -export { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '@kbn/stack-connectors-plugin/common'; export type Section = 'connectors' | 'rules' | 'alerts' | 'logs'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/index.ts index 0e1a20c666ef..8bb75e3cc4db 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/index.ts @@ -6,4 +6,7 @@ */ export { templateActionVariable } from './template_action_variable'; +export { hasMustacheTokens } from './has_mustache_tokens'; +export { AlertProvidedActionVariables } from './action_variables'; +export { updateActionConnector } from './action_connector_api'; export { isRuleSnoozed } from './is_rule_snoozed'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.test.tsx index b4cd8d1b979a..5c2f9db81689 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.test.tsx @@ -6,10 +6,7 @@ */ import React, { lazy } from 'react'; -import { - AppMockRenderer, - createAppMockRenderer, -} from '../../components/builtin_action_types/test_utils'; +import { AppMockRenderer, createAppMockRenderer } from '../../components/test_utils'; import { ConnectorForm } from './connector_form'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import userEvent from '@testing-library/user-event'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.test.tsx index 747a4925d35f..dd99bbdbabed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.test.tsx @@ -11,7 +11,7 @@ import { AppMockRenderer, createAppMockRenderer, FormTestProvider, -} from '../../components/builtin_action_types/test_utils'; +} from '../../components/test_utils'; import { ConnectorFormFields } from './connector_form_fields'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { waitFor } from '@testing-library/dom'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.test.tsx index 9df345c45f1e..04cbad726ead 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, act } from '@testing-library/react'; -import { FormTestProvider } from '../../components/builtin_action_types/test_utils'; +import { FormTestProvider } from '../../components/test_utils'; import { ConnectorFormFieldsGlobal } from './connector_form_fields_global'; describe('ConnectorFormFieldsGlobal', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.test.tsx index a0ca1a501dc9..6256ee29aee4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.test.tsx @@ -11,10 +11,7 @@ import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import userEvent from '@testing-library/user-event'; import { waitFor } from '@testing-library/dom'; import { act } from '@testing-library/react'; -import { - AppMockRenderer, - createAppMockRenderer, -} from '../../../components/builtin_action_types/test_utils'; +import { AppMockRenderer, createAppMockRenderer } from '../../../components/test_utils'; import CreateConnectorFlyout from '.'; import { betaBadgeProps } from '../beta_badge_props'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx index 23793d3c5766..715279fa67b1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx @@ -11,10 +11,7 @@ import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import userEvent from '@testing-library/user-event'; import { waitFor } from '@testing-library/dom'; import { act } from '@testing-library/react'; -import { - AppMockRenderer, - createAppMockRenderer, -} from '../../../components/builtin_action_types/test_utils'; +import { AppMockRenderer, createAppMockRenderer } from '../../../components/test_utils'; import EditConnectorFlyout from '.'; import { ActionConnector, EditConnectorTabs, GenericValidationResult } from '../../../../types'; import { betaBadgeProps } from '../beta_badge_props'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx index 6c55cd34c065..41893e785f5b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { FormTestProvider } from '../../components/builtin_action_types/test_utils'; +import { FormTestProvider } from '../../components/test_utils'; import { EncryptedFieldsCallout } from './encrypted_fields_callout'; import { render, RenderResult } from '@testing-library/react'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts index 993139926b69..d83235153860 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts @@ -26,3 +26,5 @@ export const ConnectorAddModal = suspendedComponentWithProps import('./connector_add_inline')) ); + +export type { ConnectorFormSchema } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index.ts index fd43a6baf3f0..67677fa83b51 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index.ts @@ -21,7 +21,8 @@ export { builtInAggregationTypes, builtInGroupByTypes, } from './constants'; +export { connectorDeprecatedMessage, deprecatedMessage } from './connectors_selection'; export type { IOption } from './index_controls'; export { getFields, getIndexOptions, firstFieldOption } from './index_controls'; -export { getTimeFieldOptions } from './lib'; +export { getTimeFieldOptions, useKibana } from './lib'; export type { Comparator, AggregationType, GroupByType, RuleStatus } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts index c8f7a6e46859..f7af1fbbde25 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts @@ -6,3 +6,4 @@ */ export { getTimeFieldOptions, getTimeOptions } from './get_time_options'; +export { useKibana } from './kibana'; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 12f45dcc31e4..7500a66b70f1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -43,12 +43,51 @@ export type { RulesListVisibleColumns, } from './types'; +export type { + ActionConnectorFieldsProps, + ActionParamsProps, + ActionTypeModel, + GenericValidationResult, +} from './types'; + +export { + AlertHistoryDefaultIndexName, + ALERT_HISTORY_PREFIX, + AlertHistoryDocumentTemplate, + AlertHistoryEsIndexConnectorId, +} from './types'; + +export { useConnectorContext } from './application/context/use_connector_context'; + export { ActionForm, CreateConnectorFlyout, EditConnectorFlyout, } from './application/sections/action_connector_form'; +export type { ConnectorFormSchema } from './application/sections/action_connector_form'; + +export type { ConfigFieldSchema, SecretsFieldSchema } from './application/components'; + +export { + ButtonGroupField, + HiddenField, + JsonEditorWithMessageVariables, + JsonFieldWrapper, + MustacheTextFieldWrapper, + PasswordField, + SimpleConnectorForm, + TextAreaWithMessageVariables, + TextFieldWithMessageVariables, +} from './application/components'; + +export { + AlertProvidedActionVariables, + hasMustacheTokens, + templateActionVariable, + updateActionConnector, +} from './application/lib'; + export type { ActionGroupWithCondition } from './application/sections'; export { AlertConditions, AlertConditionsGroup } from './application/sections'; @@ -57,6 +96,7 @@ export function plugin(context: PluginInitializerContext) { return new Plugin(context); } +export { useKibana } from './common'; export type { AggregationType, Comparator } from './common'; export { @@ -74,6 +114,8 @@ export { getTimeFieldOptions, GroupByExpression, COMPARATORS, + connectorDeprecatedMessage, + deprecatedMessage, } from './common'; export type { diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index b44ef00c5f42..02722bc0ee73 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -5,14 +5,12 @@ * 2.0. */ -import type { ValidatedEmail } from '@kbn/actions-plugin/common'; import type { TriggersAndActionsUIPublicPluginStart } from './plugin'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; -import { RegistrationServices } from './application/components/builtin_action_types'; import { TypeRegistry } from './application/type_registry'; import { ActionTypeModel, @@ -132,9 +130,3 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { export const triggersActionsUiMock = { createStart: createStartMock, }; - -function validateEmailAddresses(addresses: string[]): ValidatedEmail[] { - return addresses.map((address) => ({ address, valid: true })); -} - -export const registrationServicesMock: RegistrationServices = { validateEmailAddresses }; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 5cc4dbb0ece8..10c5e5637f15 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -23,7 +23,6 @@ import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { registerBuiltInActionTypes } from './application/components/builtin_action_types'; import { TypeRegistry } from './application/type_registry'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; @@ -248,13 +247,6 @@ export class Plugin }, }); - registerBuiltInActionTypes({ - actionTypeRegistry: this.actionTypeRegistry, - services: { - validateEmailAddresses: plugins.actions.validateEmailAddresses, - }, - }); - if (this.experimentalFeatures.internalAlertsTable) { registerAlertsTableConfiguration({ alertsTableConfigurationRegistry: this.alertsTableConfigurationRegistry, diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index 991cbc5b0121..8618be6c9c28 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -16,7 +16,6 @@ "references": [ { "path": "../../../src/core/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, - { "path": "../stack_connectors/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../rule_registry/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, From 7f5c804b5cfcc176d676a516808881ef594389fd Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Tue, 27 Sep 2022 09:48:56 -0500 Subject: [PATCH 017/185] [TIP] Reorganize flyout folder within indicators module (#141848) --- .../empty_prompt/empty_prompt.stories.tsx} | 4 ++-- .../empty_prompt/empty_prompt.tsx} | 3 +-- .../index.tsx => flyout/empty_prompt/index.ts} | 2 +- .../fields_table/fields_table.stories.tsx} | 10 +++++----- .../fields_table/fields_table.tsx} | 6 +++--- .../index.tsx => flyout/fields_table/index.ts} | 2 +- .../flyout.stories.tsx} | 2 +- .../flyout.test.tsx} | 2 +- .../indicators_flyout.tsx => flyout/flyout.tsx} | 6 +++--- .../index.tsx => flyout/index.ts} | 2 +- .../indicator_value_actions/index.ts} | 0 .../indicator_value_actions.tsx | 12 ++++++------ .../components/flyout/json_tab/index.ts | 8 ++++++++ .../json_tab/json_tab.stories.tsx} | 4 ++-- .../json_tab/json_tab.test.tsx} | 8 ++++---- .../json_tab/json_tab.tsx} | 4 ++-- .../overview_tab/block/block.stories.tsx} | 8 ++++---- .../overview_tab/block/block.tsx} | 10 +++++----- .../components/flyout/overview_tab/block/index.ts | 8 ++++++++ .../highlighted_values_table.tsx | 6 +++--- .../highlighted_values_table/index.ts} | 0 .../components/flyout/overview_tab/index.ts | 8 ++++++++ .../overview_tab/overview_tab.stories.tsx} | 8 ++++---- .../overview_tab/overview_tab.test.tsx} | 8 ++++---- .../overview_tab/overview_tab.tsx} | 15 +++++++-------- .../block => flyout/table_tab}/index.tsx | 2 +- .../table_tab/table_tab.stories.tsx} | 12 ++++++------ .../table_tab/table_tab.test.tsx} | 10 +++++----- .../table_tab/table_tab.tsx} | 6 +++--- .../components/indicator_empty_prompt/index.tsx | 8 -------- .../tabs/indicators_flyout_overview/index.tsx | 8 -------- .../tabs/indicators_flyout_table/index.tsx | 8 -------- .../components/indicators_table/cell_renderer.tsx | 2 +- .../indicators_table/indicators_table.test.tsx | 4 ++-- .../indicators_table/indicators_table.tsx | 2 +- 35 files changed, 103 insertions(+), 105 deletions(-) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.stories.tsx => flyout/empty_prompt/empty_prompt.stories.tsx} (78%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.tsx => flyout/empty_prompt/empty_prompt.tsx} (94%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/index.tsx => flyout/empty_prompt/index.ts} (87%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/components/indicator_fields_table/indicator_fields_table.stories.tsx => flyout/fields_table/fields_table.stories.tsx} (65%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/components/indicator_fields_table/indicator_fields_table.tsx => flyout/fields_table/fields_table.tsx} (89%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_json/index.tsx => flyout/fields_table/index.ts} (85%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/indicators_flyout.stories.tsx => flyout/flyout.stories.tsx} (97%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/indicators_flyout.test.tsx => flyout/flyout.test.tsx} (98%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/indicators_flyout.tsx => flyout/flyout.tsx} (94%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/components/indicator_fields_table/index.tsx => flyout/index.ts} (85%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicator_value_actions/index.tsx => flyout/indicator_value_actions/index.ts} (100%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{ => flyout}/indicator_value_actions/indicator_value_actions.tsx (86%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/index.ts rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.stories.tsx => flyout/json_tab/json_tab.stories.tsx} (88%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.test.tsx => flyout/json_tab/json_tab.test.tsx} (82%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.tsx => flyout/json_tab/json_tab.tsx} (86%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.stories.tsx => flyout/overview_tab/block/block.stories.tsx} (70%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.tsx => flyout/overview_tab/block/block.tsx} (82%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/index.ts rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/components => flyout/overview_tab}/highlighted_values_table/highlighted_values_table.tsx (88%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/index.tsx => flyout/overview_tab/highlighted_values_table/index.ts} (100%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/index.ts rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.stories.tsx => flyout/overview_tab/overview_tab.stories.tsx} (79%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.test.tsx => flyout/overview_tab/overview_tab.test.tsx} (86%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.tsx => flyout/overview_tab/overview_tab.tsx} (87%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_overview/components/block => flyout/table_tab}/index.tsx (87%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.stories.tsx => flyout/table_tab/table_tab.stories.tsx} (72%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.test.tsx => flyout/table_tab/table_tab.test.tsx} (82%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.tsx => flyout/table_tab/table_tab.tsx} (82%) delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/index.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/index.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/index.tsx diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.stories.tsx similarity index 78% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.stories.tsx index 56d66781d187..3f30e6c223cd 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.stories.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { Story } from '@storybook/react'; -import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers'; -import { IndicatorEmptyPrompt } from './indicator_empty_prompt'; +import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; +import { IndicatorEmptyPrompt } from '.'; export default { component: IndicatorEmptyPrompt, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.tsx similarity index 94% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.tsx index 0edf3e67f3c0..7242989ad86a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/indicator_empty_prompt.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/empty_prompt.tsx @@ -7,8 +7,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import React from 'react'; -import { VFC } from 'react'; +import React, { VFC } from 'react'; export const EMPTY_PROMPT_TEST_ID = 'indicatorEmptyPrompt'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/index.ts similarity index 87% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/index.ts index 9e055caf749a..3c3d20d367b8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/empty_prompt/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicators_flyout'; +export * from './empty_prompt'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx similarity index 65% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx index c867eda97389..8d3f13ae01af 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx @@ -6,11 +6,11 @@ */ import React from 'react'; -import { mockIndicatorsFiltersContext } from '../../../../../../common/mocks/mock_indicators_filters_context'; -import { IndicatorFieldsTable } from './indicator_fields_table'; -import { generateMockIndicator } from '../../../../../../../common/types/indicator'; -import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers'; -import { IndicatorsFiltersContext } from '../../../../context'; +import { mockIndicatorsFiltersContext } from '../../../../../common/mocks/mock_indicators_filters_context'; +import { IndicatorFieldsTable } from '.'; +import { generateMockIndicator } from '../../../../../../common/types/indicator'; +import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; +import { IndicatorsFiltersContext } from '../../../context'; export default { component: IndicatorFieldsTable, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx similarity index 89% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx index 13bb919009ba..e670d6b90e02 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/indicator_fields_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx @@ -8,9 +8,9 @@ import { EuiBasicTableColumn, EuiInMemoryTable, EuiInMemoryTableProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo, VFC } from 'react'; -import { Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorFieldValue } from '../../../indicator_field_value'; -import { IndicatorValueActions } from '../../../indicator_value_actions'; +import { Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorFieldValue } from '../../indicator_field_value'; +import { IndicatorValueActions } from '../indicator_value_actions'; export interface IndicatorFieldsTableProps { fields: string[]; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/index.ts similarity index 85% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/index.ts index 7e927ef8fab0..aea9d041e18b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicators_flyout_json'; +export * from './fields_table'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx similarity index 97% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx index ec87259c90a5..12345056c7a9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx @@ -13,7 +13,7 @@ import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indi import { mockUiSettingsService } from '../../../../common/mocks/mock_kibana_ui_settings_service'; import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; -import { IndicatorsFlyout } from './indicators_flyout'; +import { IndicatorsFlyout } from '.'; import { IndicatorsFiltersContext } from '../../context'; export default { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.test.tsx similarity index 98% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.test.tsx index 08add53bafcb..a50cf08b3f2b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { cleanup, render, screen } from '@testing-library/react'; -import { IndicatorsFlyout, SUBTITLE_TEST_ID, TITLE_TEST_ID } from './indicators_flyout'; +import { IndicatorsFlyout, SUBTITLE_TEST_ID, TITLE_TEST_ID } from '.'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx similarity index 94% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx index 86949da21a81..24fe1cc0082e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/indicators_flyout.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx @@ -24,10 +24,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { InvestigateInTimelineButton } from '../../../timeline/components/investigate_in_timeline_button'; import { DateFormatter } from '../../../../components/date_formatter/date_formatter'; import { Indicator, RawIndicatorFieldId } from '../../../../../common/types/indicator'; -import { IndicatorsFlyoutJson } from './tabs/indicators_flyout_json/indicators_flyout_json'; -import { IndicatorsFlyoutTable } from './tabs/indicators_flyout_table/indicators_flyout_table'; +import { IndicatorsFlyoutJson } from './json_tab'; +import { IndicatorsFlyoutTable } from './table_tab'; import { unwrapValue } from '../../utils/unwrap_value'; -import { IndicatorsFlyoutOverview } from './tabs/indicators_flyout_overview'; +import { IndicatorsFlyoutOverview } from './overview_tab'; export const TITLE_TEST_ID = 'tiIndicatorFlyoutTitle'; export const SUBTITLE_TEST_ID = 'tiIndicatorFlyoutSubtitle'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/index.ts similarity index 85% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/index.ts index 9252945c8f55..6a2c75f0054a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_fields_table/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicator_fields_table'; +export * from './flyout'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_value_actions/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/index.ts similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_value_actions/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/index.ts diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_value_actions/indicator_value_actions.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx similarity index 86% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_value_actions/indicator_value_actions.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx index 42fd697ec539..919b39da28c3 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_value_actions/indicator_value_actions.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx @@ -6,13 +6,13 @@ */ import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; -import React, { VFC } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; -import { Indicator } from '../../../../../common/types/indicator'; -import { FilterIn } from '../../../query_bar/components/filter_in'; -import { FilterOut } from '../../../query_bar/components/filter_out'; -import { AddToTimeline } from '../../../timeline/components/add_to_timeline'; -import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../utils/field_value'; +import React, { VFC } from 'react'; +import { Indicator } from '../../../../../../common/types/indicator'; +import { FilterIn } from '../../../../query_bar/components/filter_in'; +import { FilterOut } from '../../../../query_bar/components/filter_out'; +import { AddToTimeline } from '../../../../timeline/components/add_to_timeline'; +import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../../utils/field_value'; export const TIMELINE_BUTTON_TEST_ID = 'TimelineButton'; export const FILTER_IN_BUTTON_TEST_ID = 'FilterInButton'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/index.ts new file mode 100644 index 000000000000..2d4825141142 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './json_tab'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.stories.tsx similarity index 88% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.stories.tsx index 1e40c23a26d4..8d2eead239f4 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.stories.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { Story } from '@storybook/react'; -import { generateMockIndicator, Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorsFlyoutJson } from './indicators_flyout_json'; +import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorsFlyoutJson } from '.'; export default { component: IndicatorsFlyoutJson, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.test.tsx similarity index 82% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.test.tsx index a468db60a023..d56b328c6159 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.test.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { TestProvidersComponent } from '../../../../../../common/mocks/test_providers'; -import { generateMockIndicator, Indicator } from '../../../../../../../common/types/indicator'; -import { CODE_BLOCK_TEST_ID, IndicatorsFlyoutJson } from './indicators_flyout_json'; +import { TestProvidersComponent } from '../../../../../common/mocks/test_providers'; +import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; +import { CODE_BLOCK_TEST_ID, IndicatorsFlyoutJson } from '.'; +import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt'; const mockIndicator: Indicator = generateMockIndicator(); -import { EMPTY_PROMPT_TEST_ID } from '../../components/indicator_empty_prompt'; describe('', () => { it('should render code block component on valid indicator', () => { const { getByTestId } = render( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.tsx similarity index 86% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.tsx index 99c4bcfb0d50..f7dc6ad59de0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_json/indicators_flyout_json.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/json_tab/json_tab.tsx @@ -7,8 +7,8 @@ import React, { VFC } from 'react'; import { EuiCodeBlock } from '@elastic/eui'; -import { Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorEmptyPrompt } from '../../components/indicator_empty_prompt'; +import { Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorEmptyPrompt } from '../empty_prompt'; export const CODE_BLOCK_TEST_ID = 'tiFlyoutJsonCodeBlock'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx similarity index 70% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx index 32966e72c2ee..1049518f4620 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx @@ -6,10 +6,10 @@ */ import React from 'react'; -import { IndicatorsFiltersContext } from '../../../../../../context'; -import { StoryProvidersComponent } from '../../../../../../../../common/mocks/story_providers'; -import { generateMockIndicator } from '../../../../../../../../../common/types/indicator'; -import { IndicatorBlock } from './indicator_block'; +import { IndicatorsFiltersContext } from '../../../../context'; +import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers'; +import { generateMockIndicator } from '../../../../../../../common/types/indicator'; +import { IndicatorBlock } from '.'; export default { component: IndicatorBlock, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx similarity index 82% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx index 9537131a574a..dd8d4335feca 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/indicator_block.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx @@ -7,11 +7,11 @@ import { EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; import React, { VFC } from 'react'; -import { euiStyled, css } from '@kbn/kibana-react-plugin/common'; -import { Indicator } from '../../../../../../../../../common/types/indicator'; -import { IndicatorFieldValue } from '../../../../../indicator_field_value'; -import { IndicatorFieldLabel } from '../../../../../indicator_field_label'; -import { IndicatorValueActions } from '../../../../../indicator_value_actions'; +import { css, euiStyled } from '@kbn/kibana-react-plugin/common'; +import { Indicator } from '../../../../../../../common/types/indicator'; +import { IndicatorFieldValue } from '../../../indicator_field_value'; +import { IndicatorFieldLabel } from '../../../indicator_field_label'; +import { IndicatorValueActions } from '../../indicator_value_actions'; /** * Show actions wrapper on hover. This is a helper component, limited only to Block diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/index.ts new file mode 100644 index 000000000000..e8b564b29af9 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './block'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/highlighted_values_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx similarity index 88% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/highlighted_values_table.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx index d9af696065db..6ce9c332d632 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/highlighted_values_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx @@ -6,9 +6,9 @@ */ import React, { useMemo, VFC } from 'react'; -import { Indicator, RawIndicatorFieldId } from '../../../../../../../../../common/types/indicator'; -import { unwrapValue } from '../../../../../../utils/unwrap_value'; -import { IndicatorFieldsTable } from '../../../../components/indicator_fields_table'; +import { Indicator, RawIndicatorFieldId } from '../../../../../../../common/types/indicator'; +import { unwrapValue } from '../../../../utils/unwrap_value'; +import { IndicatorFieldsTable } from '../../fields_table'; /** * Pick indicator fields starting with the indicator type diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/index.ts similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/highlighted_values_table/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/index.ts diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/index.ts new file mode 100644 index 000000000000..4f58be52f6ba --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './overview_tab'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx similarity index 79% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx index 72b20f769575..d543b6b6d112 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx @@ -7,10 +7,10 @@ import React from 'react'; import { Story } from '@storybook/react'; -import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers'; -import { generateMockIndicator, Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorsFlyoutOverview } from './indicators_flyout_overview'; -import { IndicatorsFiltersContext } from '../../../../context'; +import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; +import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorsFlyoutOverview } from '.'; +import { IndicatorsFiltersContext } from '../../../context'; export default { component: IndicatorsFlyoutOverview, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.test.tsx similarity index 86% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.test.tsx index 580534e5668c..df4201761a98 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.test.tsx @@ -5,16 +5,16 @@ * 2.0. */ -import { TestProvidersComponent } from '../../../../../../common/mocks/test_providers'; +import { TestProvidersComponent } from '../../../../../common/mocks/test_providers'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { generateMockIndicator, Indicator } from '../../../../../../../common/types/indicator'; +import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; import { IndicatorsFlyoutOverview, TI_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS, TI_FLYOUT_OVERVIEW_TABLE, -} from './indicators_flyout_overview'; -import { EMPTY_PROMPT_TEST_ID } from '../../components/indicator_empty_prompt'; +} from '.'; +import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt'; describe('', () => { describe('invalid indicator', () => { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.tsx similarity index 87% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.tsx index a7551398b62c..7abbc1508fb5 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/indicators_flyout_overview.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.tsx @@ -15,14 +15,13 @@ import { EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useMemo } from 'react'; -import { VFC } from 'react'; -import { EMPTY_VALUE } from '../../../../../../../common/constants'; -import { Indicator, RawIndicatorFieldId } from '../../../../../../../common/types/indicator'; -import { unwrapValue } from '../../../../utils/unwrap_value'; -import { IndicatorEmptyPrompt } from '../../components/indicator_empty_prompt'; -import { IndicatorBlock } from './components/block'; -import { HighlightedValuesTable } from './components/highlighted_values_table'; +import React, { useMemo, VFC } from 'react'; +import { EMPTY_VALUE } from '../../../../../../common/constants'; +import { Indicator, RawIndicatorFieldId } from '../../../../../../common/types/indicator'; +import { unwrapValue } from '../../../utils/unwrap_value'; +import { IndicatorEmptyPrompt } from '../empty_prompt'; +import { IndicatorBlock } from './block'; +import { HighlightedValuesTable } from './highlighted_values_table'; const highLevelFields = [ RawIndicatorFieldId.Feed, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/index.tsx similarity index 87% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/index.tsx index dbad4c02ee5d..73571446eeef 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/components/block/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/index.tsx @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicator_block'; +export * from './table_tab'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx similarity index 72% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx index 3a3aa1fa788e..014d57b8ec11 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx @@ -9,12 +9,12 @@ import React from 'react'; import { Story } from '@storybook/react'; import { CoreStart } from '@kbn/core/public'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { mockIndicatorsFiltersContext } from '../../../../../../common/mocks/mock_indicators_filters_context'; -import { mockUiSettingsService } from '../../../../../../common/mocks/mock_kibana_ui_settings_service'; -import { mockKibanaTimelinesService } from '../../../../../../common/mocks/mock_kibana_timelines_service'; -import { generateMockIndicator, Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorsFlyoutTable } from './indicators_flyout_table'; -import { IndicatorsFiltersContext } from '../../../../context'; +import { mockIndicatorsFiltersContext } from '../../../../../common/mocks/mock_indicators_filters_context'; +import { mockUiSettingsService } from '../../../../../common/mocks/mock_kibana_ui_settings_service'; +import { mockKibanaTimelinesService } from '../../../../../common/mocks/mock_kibana_timelines_service'; +import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorsFlyoutTable } from '.'; +import { IndicatorsFiltersContext } from '../../../context'; export default { component: IndicatorsFlyoutTable, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.test.tsx similarity index 82% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.test.tsx index 5e1264b1b2e4..8503bcdace2c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.test.tsx @@ -7,15 +7,15 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { TestProvidersComponent } from '../../../../../../common/mocks/test_providers'; +import { TestProvidersComponent } from '../../../../../common/mocks/test_providers'; import { generateMockIndicator, Indicator, RawIndicatorFieldId, -} from '../../../../../../../common/types/indicator'; -import { IndicatorsFlyoutTable, TABLE_TEST_ID } from './indicators_flyout_table'; -import { unwrapValue } from '../../../../utils/unwrap_value'; -import { EMPTY_PROMPT_TEST_ID } from '../../components/indicator_empty_prompt'; +} from '../../../../../../common/types/indicator'; +import { IndicatorsFlyoutTable, TABLE_TEST_ID } from '.'; +import { unwrapValue } from '../../../utils/unwrap_value'; +import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt'; const mockIndicator: Indicator = generateMockIndicator(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.tsx similarity index 82% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.tsx index 8bb7956afc17..0f0a699733cc 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/indicators_flyout_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.tsx @@ -6,9 +6,9 @@ */ import React, { VFC } from 'react'; -import { Indicator } from '../../../../../../../common/types/indicator'; -import { IndicatorEmptyPrompt } from '../../components/indicator_empty_prompt'; -import { IndicatorFieldsTable } from '../../components/indicator_fields_table'; +import { Indicator } from '../../../../../../common/types/indicator'; +import { IndicatorEmptyPrompt } from '../empty_prompt'; +import { IndicatorFieldsTable } from '../fields_table'; export const TABLE_TEST_ID = 'tiFlyoutTableTabRow'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/index.tsx deleted file mode 100644 index a2b896781739..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/components/indicator_empty_prompt/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './indicator_empty_prompt'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/index.tsx deleted file mode 100644 index 71fcb871adf4..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_overview/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './indicators_flyout_overview'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/index.tsx deleted file mode 100644 index fa8190bee836..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_flyout/tabs/indicators_flyout_table/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './indicators_flyout_table'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_renderer.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_renderer.tsx index b95a378a35a5..394d996d0ce9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_renderer.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_renderer.tsx @@ -11,7 +11,7 @@ import { euiLightVars as themeLight, euiDarkVars as themeDark } from '@kbn/ui-th import React from 'react'; import { useKibana } from '../../../../hooks/use_kibana'; import { Indicator } from '../../../../../common/types/indicator'; -import { IndicatorFieldValue } from '../indicator_field_value/indicator_field_value'; +import { IndicatorFieldValue } from '../indicator_field_value'; import { IndicatorsTableContext } from './context'; import { ActionsRowCell } from './actions_row_cell'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx index b110c0f91c31..027033ae4771 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { IndicatorsTable, IndicatorsTableProps } from './indicators_table'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; -import { BUTTON_TEST_ID } from '../open_indicator_flyout_button/open_indicator_flyout_button'; -import { TITLE_TEST_ID } from '../indicators_flyout/indicators_flyout'; +import { BUTTON_TEST_ID } from '../open_indicator_flyout_button'; +import { TITLE_TEST_ID } from '../flyout'; import { SecuritySolutionDataViewBase } from '../../../../types'; const stub = () => {}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx index f12e080b000b..d1888431f7d8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx @@ -23,7 +23,7 @@ import { Indicator, RawIndicatorFieldId } from '../../../../../common/types/indi import { cellRendererFactory } from './cell_renderer'; import { EmptyState } from '../../../../components/empty_state'; import { IndicatorsTableContext, IndicatorsTableContextValue } from './context'; -import { IndicatorsFlyout } from '../indicators_flyout/indicators_flyout'; +import { IndicatorsFlyout } from '../flyout'; import { useToolbarOptions } from './hooks/use_toolbar_options'; import { ColumnSettingsValue } from './hooks/use_column_settings'; import { useFieldTypes } from '../../../../hooks/use_field_types'; From c23b7fd44590d9c2b4262664b8280b5fc5c8b4b4 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Tue, 27 Sep 2022 16:55:54 +0200 Subject: [PATCH 018/185] revert upgrade_status change (#141937) --- x-pack/plugins/fleet/common/services/agent_status.ts | 6 +++--- .../fleet/common/services/is_agent_upgradeable.ts | 2 +- x-pack/plugins/fleet/common/types/models/agent.ts | 9 ++------- x-pack/plugins/fleet/server/services/agents/actions.ts | 2 +- .../plugins/fleet/server/services/agents/upgrade.test.ts | 6 +++--- x-pack/plugins/fleet/server/services/agents/upgrade.ts | 2 +- .../server/services/agents/upgrade_action_runner.ts | 2 +- .../server/endpoint/routes/metadata/metadata.test.ts | 4 +--- .../endpoint/routes/metadata/query_builders.fixtures.ts | 6 +----- .../routes/metadata/support/agent_status.test.ts | 4 ++-- 10 files changed, 16 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/fleet/common/services/agent_status.ts b/x-pack/plugins/fleet/common/services/agent_status.ts index 72b912e573af..55f93fca48d6 100644 --- a/x-pack/plugins/fleet/common/services/agent_status.ts +++ b/x-pack/plugins/fleet/common/services/agent_status.ts @@ -47,7 +47,7 @@ export function getAgentStatus(agent: Agent | FleetServerAgent): AgentStatus { ? agent.policy_revision_idx : undefined; - if (!policyRevision || (agent.upgrade_started_at && agent.upgrade_status !== 'completed')) { + if (!policyRevision || (agent.upgrade_started_at && !agent.upgraded_at)) { return 'updating'; } @@ -75,7 +75,7 @@ export function getPreviousAgentStatusForOfflineAgents( ? agent.policy_revision_idx : undefined; - if (!policyRevision || (agent.upgrade_started_at && agent.upgrade_status !== 'completed')) { + if (!policyRevision || (agent.upgrade_started_at && !agent.upgraded_at)) { return 'updating'; } } @@ -109,7 +109,7 @@ export function buildKueryForOfflineAgents(path: string = ''): string { } export function buildKueryForUpgradingAgents(path: string = ''): string { - return `(${path}upgrade_started_at:*) and not (${path}upgrade_status:completed)`; + return `(${path}upgrade_started_at:*) and not (${path}upgraded_at:*)`; } export function buildKueryForUpdatingAgents(path: string = ''): string { diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts index c4c9fa7e75a6..2a523d1a2eab 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts @@ -25,7 +25,7 @@ export function isAgentUpgradeable(agent: Agent, kibanaVersion: string, versionT return false; } // check that the agent is not already in the process of updating - if (agent.upgrade_started_at && agent.upgrade_status !== 'completed') { + if (agent.upgrade_started_at && !agent.upgraded_at) { return false; } if (versionToUpgrade !== undefined) { diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 9ca69b510062..5def12287b4f 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -75,9 +75,8 @@ interface AgentBase { enrolled_at: string; unenrolled_at?: string; unenrollment_started_at?: string; - upgraded_at?: string; + upgraded_at?: string | null; upgrade_started_at?: string | null; - upgrade_status?: 'started' | 'completed'; access_api_key_id?: string; default_api_key?: string; default_api_key_id?: string; @@ -188,15 +187,11 @@ export interface FleetServerAgent { /** * Date/time the Elastic Agent was last upgraded */ - upgraded_at?: string; + upgraded_at?: string | null; /** * Date/time the Elastic Agent started the current upgrade */ upgrade_started_at?: string | null; - /** - * Upgrade status - */ - upgrade_status?: 'started' | 'completed'; /** * ID of the API key the Elastic Agent must used to contact Fleet Server */ diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index 20cb2fb94e51..4a6c772b69b9 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -242,8 +242,8 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: hit._source.agents.map((agentId) => ({ agentId, data: { + upgraded_at: null, upgrade_started_at: null, - upgrade_status: 'completed', }, })) ); diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts index 9692f0582287..db880f56ef47 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts @@ -38,7 +38,7 @@ describe('sendUpgradeAgentsActions (plural)', () => { expect(ids).toEqual(idsToAction); for (const doc of docs!) { expect(doc).toHaveProperty('upgrade_started_at'); - expect(doc.upgrade_status).toEqual('started'); + expect(doc.upgraded_at).toEqual(null); } }); it('cannot upgrade from a hosted agent policy by default', async () => { @@ -60,7 +60,7 @@ describe('sendUpgradeAgentsActions (plural)', () => { expect(ids).toEqual(onlyRegular); for (const doc of docs!) { expect(doc).toHaveProperty('upgrade_started_at'); - expect(doc.upgrade_status).toEqual('started'); + expect(doc.upgraded_at).toEqual(null); } // hosted policy is updated in action results with error @@ -98,7 +98,7 @@ describe('sendUpgradeAgentsActions (plural)', () => { expect(ids).toEqual(idsToAction); for (const doc of docs!) { expect(doc).toHaveProperty('upgrade_started_at'); - expect(doc.upgrade_status).toEqual('started'); + expect(doc.upgraded_at).toEqual(null); } }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.ts index cf298ecb5997..605aa896de59 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.ts @@ -57,8 +57,8 @@ export async function sendUpgradeAgentAction({ type: 'UPGRADE', }); await updateAgent(esClient, agentId, { + upgraded_at: null, upgrade_started_at: now, - upgrade_status: 'started', }); } diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts index c757a9f1b548..a34f189871a3 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts @@ -146,8 +146,8 @@ export async function upgradeBatch( agentsToUpdate.map((agent) => ({ agentId: agent.id, data: { + upgraded_at: null, upgrade_started_at: now, - upgrade_status: 'started', }, })) ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 88d0617c1838..d03117225279 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -285,9 +285,7 @@ describe('test endpoint routes', () => { bool: { must_not: { bool: { - should: [ - { match: { 'united.agent.upgrade_status': 'completed' } }, - ], + should: [{ exists: { field: 'united.agent.upgraded_at' } }], minimum_should_match: 1, }, }, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts index edc5d681b138..fb01bd994f0d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.fixtures.ts @@ -71,11 +71,7 @@ export const expectedCompleteUnitedIndexQuery = { must_not: { bool: { should: [ - { - match: { - 'united.agent.upgrade_status': 'completed', - }, - }, + { exists: { field: 'united.agent.upgraded_at' } }, ], minimum_should_match: 1, }, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts index 875f3927ee34..6a7febe393db 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts @@ -93,7 +93,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['healthy']; const kuery = buildStatusesKuery(status); expect(kuery).toMatchInlineSnapshot( - `"(united.agent.last_checkin:* AND not ((united.agent.last_checkin < now-300s) or ((((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)) AND not ((united.agent.last_checkin < now-300s) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*))))) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*)))))"` + `"(united.agent.last_checkin:* AND not ((united.agent.last_checkin < now-300s) or ((((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)) AND not ((united.agent.last_checkin < now-300s) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*))))) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*)))))"` ); }); @@ -115,7 +115,7 @@ describe('test filtering endpoint hosts by agent status', () => { const status = ['updating']; const kuery = buildStatusesKuery(status); expect(kuery).toMatchInlineSnapshot( - `"((((united.agent.upgrade_started_at:*) and not (united.agent.upgrade_status:completed)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)) AND not ((united.agent.last_checkin < now-300s) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*)))))"` + `"((((united.agent.upgrade_started_at:*) and not (united.agent.upgraded_at:*)) or (not (united.agent.last_checkin:*)) or (united.agent.unenrollment_started_at:*) or (not united.agent.policy_revision_idx:*)) AND not ((united.agent.last_checkin < now-300s) or ((united.agent.last_checkin_status:error or united.agent.last_checkin_status:degraded) AND not ((united.agent.last_checkin < now-300s) or (united.agent.unenrollment_started_at:*)))))"` ); }); From 1289226cac76b55eaf839a502f4588c8790e190b Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 27 Sep 2022 07:58:30 -0700 Subject: [PATCH 019/185] [8.5][Elastic Defend onboarding] Fixing Typos (#141652) --- .../endpoint_policy_create_extension.tsx | 34 +++++++++---------- .../translations.ts | 18 ++++------ 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx index 9a4dc273740e..d1223ff14826 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx @@ -28,7 +28,7 @@ import { EDR_ESSENTIAL, ENDPOINT, INTERACTIVE_ONLY, - PREVENT_MALICIOUS_BEHAVIOUR, + PREVENT_MALICIOUS_BEHAVIOR, } from './translations'; const PREFIX = 'endpoint_policy_create_extension'; @@ -75,12 +75,12 @@ export const EndpointPolicyCreateExtension = memo('NGAV'); const [behaviorProtectionChecked, setBehaviorProtectionChecked] = useState(false); - const [selectedCloudEvent, setSelectedCloudEvent] = useState('INTERACTIVE_ONLY'); + const [selectedCloudEvent, setSelectedCloudEvent] = useState('ALL_EVENTS'); const [selectedEnvironment, setSelectedEnvironment] = useState('endpoint'); const initialRender = useRef(true); - // Fleet will initialize the create form with a default name for the integratin policy, however, - // for endpoint security, we want the user to explicitely type in a name, so we blank it out + // Fleet will initialize the create form with a default name for the integrating policy, however, + // for endpoint security, we want the user to explicitly type in a name, so we blank it out // only during 1st component render (thus why the eslint disabled rule below). // Default values for config are endpoint + NGAV useEffect(() => { @@ -113,7 +113,7 @@ export const EndpointPolicyCreateExtension = memo { - // Skip trigerring this onChange on the initial render + // Skip triggering this onChange on the initial render if (initialRender.current) { initialRender.current = false; } else { @@ -217,7 +217,7 @@ export const EndpointPolicyCreateExtension = memo ), @@ -251,7 +251,7 @@ export const EndpointPolicyCreateExtension = memo @@ -265,7 +265,7 @@ export const EndpointPolicyCreateExtension = memo @@ -279,7 +279,7 @@ export const EndpointPolicyCreateExtension = memo @@ -305,13 +305,13 @@ export const EndpointPolicyCreateExtension = memo } > - +
} > - + {isPlatinumPlus && ( <> @@ -350,8 +350,8 @@ export const EndpointPolicyCreateExtension = memo diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts index b50835b36995..66688371b68d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/translations.ts @@ -30,13 +30,13 @@ export const EDR_COMPLETE = i18n.translate( export const ENDPOINT = i18n.translate( 'xpack.securitySolution.createPackagePolicy.stepConfigure.endpointDropdownOption', { - defaultMessage: 'Endpoint', + defaultMessage: 'Traditional Endpoints (desktops, laptops, virtual machines)', } ); export const CLOUD_SECURITY = i18n.translate( - 'xpack.securitySolution.createPackagePolicy.stepConfigure.cloudSecurityDropdownOption', + 'xpack.securitySolution.createPackagePolicy.stepConfigure.cloudDropdownOption', { - defaultMessage: 'Cloud Security', + defaultMessage: 'Cloud Workloads (Linux servers or Kubernetes environments)', } ); export const INTERACTIVE_ONLY = i18n.translate( @@ -51,15 +51,9 @@ export const ALL_EVENTS = i18n.translate( defaultMessage: 'All events', } ); -export const PREVENT_MALWARE = i18n.translate( - 'xpack.securitySolution.createPackagePolicy.stepConfigure.cloudEventFiltersPreventionMalware', +export const PREVENT_MALICIOUS_BEHAVIOR = i18n.translate( + 'xpack.securitySolution.createPackagePolicy.stepConfigure.cloudEventFiltersPreventionMaliciousBehavior', { - defaultMessage: 'Prevent Malware', - } -); -export const PREVENT_MALICIOUS_BEHAVIOUR = i18n.translate( - 'xpack.securitySolution.createPackagePolicy.stepConfigure.cloudEventFiltersPreventionMaliciousBehaviour', - { - defaultMessage: 'Prevent Malicious Behaviour', + defaultMessage: 'Prevent Malicious Behavior', } ); From 9cd21c51dffbcb80de824d9fe437c9b8aeed3986 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Tue, 27 Sep 2022 11:04:11 -0400 Subject: [PATCH 020/185] Add ent-search-docs-team as codeowners of enterprise search internal doclinks (#141948) --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d0c6e476d6d4..5633cf2e6e1b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -352,6 +352,7 @@ x-pack/examples/files_example @elastic/kibana-app-services # Enterprise Search /x-pack/plugins/enterprise_search @elastic/enterprise-search-frontend /x-pack/test/functional_enterprise_search/ @elastic/enterprise-search-frontend +/x-pack/plugins/enterprise_search/public/applications/shared/doc_links @elastic/ent-search-docs-team # Management Experience - Deployment Management /src/plugins/dev_tools/ @elastic/platform-deployment-management From 7e469af7c6881267f528b9a3d611244415f68269 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Tue, 27 Sep 2022 11:08:46 -0400 Subject: [PATCH 021/185] [Fleet] Point APM bundling process at package storage v2 (#141944) * Point APM bundling process at package storage v2 * Add Fleet as codeowner for bundled packages task --- .github/CODEOWNERS | 1 + src/dev/build/tasks/bundle_fleet_packages.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5633cf2e6e1b..2c1a3e323a99 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -132,6 +132,7 @@ x-pack/examples/files_example @elastic/kibana-app-services /x-pack/test/fleet_api_integration @elastic/fleet /x-pack/test/fleet_cypress @elastic/fleet /x-pack/test/fleet_functional @elastic/fleet +/src/dev/build/tasks/bundle_fleet_packages.ts # APM /x-pack/plugins/apm/ @elastic/apm-ui diff --git a/src/dev/build/tasks/bundle_fleet_packages.ts b/src/dev/build/tasks/bundle_fleet_packages.ts index 4ba5e79d2992..30cfc1d22b0e 100644 --- a/src/dev/build/tasks/bundle_fleet_packages.ts +++ b/src/dev/build/tasks/bundle_fleet_packages.ts @@ -15,6 +15,10 @@ import { Task, read, downloadToDisk, unzipBuffer, createZipFile } from '../lib'; const BUNDLED_PACKAGES_DIR = 'x-pack/plugins/fleet/target/bundled_packages'; +// APM needs to directly request its versions from Package Storage v2 - this should +// be removed when Package Storage v2 is in production +const PACKAGE_STORAGE_V2_URL = 'https://epr-v2.ea-web.elastic.dev'; + interface FleetPackage { name: string; version: string; @@ -64,7 +68,12 @@ export const BundleFleetPackages: Task = { } const archivePath = `${fleetPackage.name}-${versionToWrite}.zip`; - const archiveUrl = `${eprUrl}/epr/${fleetPackage.name}/${fleetPackage.name}-${fleetPackage.version}.zip`; + let archiveUrl = `${eprUrl}/epr/${fleetPackage.name}/${fleetPackage.name}-${fleetPackage.version}.zip`; + + // Point APM to package storage v2 + if (fleetPackage.name === 'apm') { + archiveUrl = `${PACKAGE_STORAGE_V2_URL}/epr/${fleetPackage.name}/${fleetPackage.name}-${fleetPackage.version}.zip`; + } const destination = build.resolvePath(BUNDLED_PACKAGES_DIR, archivePath); From 0446e0100bafff7255cc7e8581ad82a5dc4a91ca Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Tue, 27 Sep 2022 17:09:49 +0200 Subject: [PATCH 022/185] [ML] Stabilize docs screenshots suites (#141755) This PR stabilizes the docs screenshots suite by disabling Kibana tour popovers and wrapping the multi selection service method into a retry. --- .../test/functional/services/ml/common_ui.ts | 35 +++++++++++-------- .../functional/services/ml/test_resources.ts | 8 +++++ .../screenshot_creation/apps/ml_docs/index.ts | 2 ++ .../apps/response_ops_docs/index.ts | 2 ++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index 0d8ee7d11a8b..ef69f909437c 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -164,22 +164,27 @@ export function MachineLearningCommonUIProvider({ }, async setMultiSelectFilter(testDataSubj: string, fieldTypes: string[]) { - await testSubjects.clickWhenNotDisabledWithoutRetry(`${testDataSubj}-button`); - await testSubjects.existOrFail(`${testDataSubj}-popover`); - await testSubjects.existOrFail(`${testDataSubj}-searchInput`); - const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); + await retry.tryForTime(60 * 1000, async () => { + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); - for (const fieldType of fieldTypes) { - await retry.tryForTime(5000, async () => { - await searchBarInput.clearValueWithKeyboard(); - await searchBarInput.type(fieldType); - if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}-checked`))) { - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); - await testSubjects.click(`${testDataSubj}-option-${fieldType}`); - await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); - } - }); - } + await testSubjects.clickWhenNotDisabledWithoutRetry(`${testDataSubj}-button`); + await testSubjects.existOrFail(`${testDataSubj}-popover`); + await testSubjects.existOrFail(`${testDataSubj}-searchInput`); + const searchBarInput = await testSubjects.find(`${testDataSubj}-searchInput`); + + for (const fieldType of fieldTypes) { + await retry.tryForTime(5000, async () => { + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(fieldType); + if (!(await testSubjects.exists(`${testDataSubj}-option-${fieldType}-checked`))) { + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}`); + await testSubjects.click(`${testDataSubj}-option-${fieldType}`); + await testSubjects.existOrFail(`${testDataSubj}-option-${fieldType}-checked`); + } + }); + } + }); // escape popover await browser.pressKeys(browser.keys.ESCAPE); diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts index 4fccde6712e6..d1a7557caf2b 100644 --- a/x-pack/test/functional/services/ml/test_resources.ts +++ b/x-pack/test/functional/services/ml/test_resources.ts @@ -46,6 +46,14 @@ export function MachineLearningTestResourcesProvider( await kibanaServer.uiSettings.unset('dateFormat:tz'); }, + async disableKibanaAnnouncements() { + await kibanaServer.uiSettings.update({ hideAnnouncements: true }); + }, + + async resetKibanaAnnouncements() { + await kibanaServer.uiSettings.unset('hideAnnouncements'); + }, + async savedObjectExistsById(id: string, objectType: SavedObjectType): Promise { const response = await supertest.get(`/api/saved_objects/${objectType}/${id}`); return response.status === 200; diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/index.ts b/x-pack/test/screenshot_creation/apps/ml_docs/index.ts index 1c12efc89caf..806414939cd8 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/index.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/index.ts @@ -22,6 +22,7 @@ export default function ({ getPageObject, getService, loadTestFile }: FtrProvide before(async () => { await ml.testResources.installAllKibanaSampleData(); await ml.testResources.setKibanaTimeZoneToUTC(); + await ml.testResources.disableKibanaAnnouncements(); await browser.setWindowSize(1920, 1080); }); @@ -29,6 +30,7 @@ export default function ({ getPageObject, getService, loadTestFile }: FtrProvide await securityPage.forceLogout(); await ml.testResources.removeAllKibanaSampleData(); await ml.testResources.resetKibanaTimeZone(); + await ml.testResources.resetKibanaAnnouncements(); }); loadTestFile(require.resolve('./anomaly_detection')); diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/index.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/index.ts index 64ed2599d65b..e836e3e63c9b 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/index.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/index.ts @@ -23,6 +23,7 @@ export default function ({ getPageObject, getService, loadTestFile }: FtrProvide before(async () => { await ml.testResources.installAllKibanaSampleData(); await ml.testResources.setKibanaTimeZoneToUTC(); + await ml.testResources.disableKibanaAnnouncements(); await browser.setWindowSize(1920, 1080); await securityPage.login( esTestConfig.getUrlParts().username, @@ -34,6 +35,7 @@ export default function ({ getPageObject, getService, loadTestFile }: FtrProvide await securityPage.forceLogout(); await ml.testResources.removeAllKibanaSampleData(); await ml.testResources.resetKibanaTimeZone(); + await ml.testResources.resetKibanaAnnouncements(); }); loadTestFile(require.resolve('./stack_cases')); From f585dd6e5a3d0336d2a97fd0c5ee3ff88ccf0ab0 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Tue, 27 Sep 2022 16:14:22 +0100 Subject: [PATCH 023/185] [Security Solution][Alerts] saved query UX changes follow-up (#141747) ## Summary addresses feedback left during code review for https://github.com/elastic/kibana/pull/140064 - Saved query checkbox label shows saved query name - `createSavedQuery` moved to `x-pack/plugins/security_solution/cypress/tasks/api_calls/saved_queries.ts` - `useGetSavedQuery` moved to `x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details` - additional comments and minor code changes ### Before Screenshot 2022-09-26 at 11 38 05 ### After Screenshot 2022-09-26 at 11 37 23 --- .../custom_saved_query_rule.cy.ts | 4 ++-- .../cypress/tasks/api_calls/saved_queries.ts | 17 +++++++++++++++++ .../security_solution/cypress/tasks/common.ts | 17 ----------------- .../public/common/hooks/translations.ts | 7 ------- .../components/rules/query_bar/index.tsx | 1 + .../components/rules/step_define_rule/index.tsx | 7 +++++-- .../rules/step_define_rule/schema.tsx | 6 ------ .../rules/step_define_rule/translations.tsx | 13 +++++++++++-- .../detection_engine/rules/details/index.tsx | 4 +++- .../rules/details/translations.ts | 7 +++++++ .../rules/details}/use_get_saved_query.ts | 9 +++++---- .../lib/detection_engine/signals/get_filter.ts | 5 +---- 12 files changed, 52 insertions(+), 45 deletions(-) rename x-pack/plugins/security_solution/public/{common/hooks => detections/pages/detection_engine/rules/details}/use_get_saved_query.ts (81%) diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts index 9b1d5c71a6fa..5027fe09a8d3 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts @@ -25,8 +25,8 @@ import { import { goToRuleDetails, editFirstRule } from '../../tasks/alerts_detection_rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; -import { createSavedQuery } from '../../tasks/api_calls/saved_queries'; -import { cleanKibana, deleteAlertsAndRules, deleteSavedQueries } from '../../tasks/common'; +import { createSavedQuery, deleteSavedQueries } from '../../tasks/api_calls/saved_queries'; +import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/saved_queries.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/saved_queries.ts index 8c4aea51ec04..08867cf55d4c 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/saved_queries.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/saved_queries.ts @@ -33,3 +33,20 @@ export const createSavedQuery = ( }, headers: { 'kbn-xsrf': 'cypress-creds' }, }); + +export const deleteSavedQueries = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + cy.request('POST', `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, { + query: { + bool: { + filter: [ + { + match: { + type: 'query', + }, + }, + ], + }, + }, + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index 23f04d0fd3c2..cd9525e95b0b 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -179,23 +179,6 @@ export const deleteCases = () => { }); }; -export const deleteSavedQueries = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - cy.request('POST', `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, { - query: { - bool: { - filter: [ - { - match: { - type: 'query', - }, - }, - ], - }, - }, - }); -}; - export const postDataView = (dataSource: string) => { cy.request({ method: 'POST', diff --git a/x-pack/plugins/security_solution/public/common/hooks/translations.ts b/x-pack/plugins/security_solution/public/common/hooks/translations.ts index 1f939282b5c7..54ed3a79d017 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/translations.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/translations.ts @@ -39,10 +39,3 @@ export const EQL_TIME_INTERVAL_NOT_DEFINED = i18n.translate( defaultMessage: 'Time intervals are not defined.', } ); - -export const SAVED_QUERY_LOAD_ERROR_TOAST = i18n.translate( - 'xpack.securitySolution.hooks.useGetSavedQuery.errorToastMessage', - { - defaultMessage: 'Failed to load the saved query', - } -); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx index a5316eabb11f..7d7c9534c168 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx @@ -70,6 +70,7 @@ const savedQueryToFieldValue = (savedQuery: SavedQuery): FieldValueQueryBar => ( filters: savedQuery.attributes.filters ?? [], query: savedQuery.attributes.query, saved_id: savedQuery.id, + title: savedQuery.attributes.title, }); export const QueryBarDefineRule = ({ diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 23042cd1165e..21bf8125540b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -739,8 +739,8 @@ const StepDefineRuleComponent: FC = ({ <> = ({ 'data-test-subj': 'detectionEngineStepDefineRuleShouldLoadQueryDynamically', euiFieldProps: { disabled: isLoading, + label: formQuery?.title + ? i18n.getSavedQueryCheckboxLabel(formQuery.title) + : undefined, }, }} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx index bc3937013b6a..b23b496eae82 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx @@ -629,12 +629,6 @@ export const schema: FormSchema = { }, shouldLoadQueryDynamically: { type: FIELD_TYPES.CHECKBOX, - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldShouldLoadQueryDynamicallyLabel', - { - defaultMessage: 'Load the saved query dynamically on each rule execution', - } - ), defaultValue: false, }, }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx index b28d06777fd3..4650da2ce603 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx @@ -78,13 +78,22 @@ export const EQL_QUERY_BAR_LABEL = i18n.translate( } ); -export const SAVED_QUERY_CHECKBOX_LABEL = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.SavedQueryCheckboxLabel', +export const SAVED_QUERY_FORM_ROW_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.SavedQueryFormRowLabel', { defaultMessage: 'Saved query', } ); +export const getSavedQueryCheckboxLabel = (savedQueryName: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldShouldLoadQueryDynamicallyLabel', + { + defaultMessage: 'Load saved query "{savedQueryName}" dynamically on each rule execution', + values: { savedQueryName }, + } + ); + export const THREAT_MATCH_INDEX_HELPER_TEXT = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription', { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index e91756ce74db..5149269c57d9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -116,7 +116,7 @@ import { ExecutionLogTable } from './execution_log_table/execution_log_table'; import * as detectionI18n from '../../translations'; import * as ruleI18n from '../translations'; import { RuleDetailsContextProvider } from './rule_details_context'; -import { useGetSavedQuery } from '../../../../../common/hooks/use_get_saved_query'; +import { useGetSavedQuery } from './use_get_saved_query'; import * as i18n from './translations'; import { NeedAdminForUpdateRulesCallOut } from '../../../../components/callouts/need_admin_for_update_callout'; import { MissingPrivilegesCallOut } from '../../../../components/callouts/missing_privileges_callout'; @@ -307,6 +307,8 @@ const RuleDetailsPageComponent: React.FC = ({ const [dataViewOptions, setDataViewOptions] = useState<{ [x: string]: DataViewListItem }>({}); // load saved query only if rule type === 'saved_query', as other rule types still can have saved_id property that is not used + // Rule schema allows to save any rule with saved_id property, but it only used for saved_query rule type + // In future we might look in possibility to restrict rule schema (breaking change!) and remove saved_id from the rest of rules through migration const savedQueryId = rule?.type === 'saved_query' ? rule?.saved_id : undefined; const { isSavedQueryLoading, savedQueryBar } = useGetSavedQuery(savedQueryId); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts index 1a50d570ea17..b1c7e0033f5f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/translations.ts @@ -69,3 +69,10 @@ export const DELETED_RULE = i18n.translate( defaultMessage: 'Deleted rule', } ); + +export const SAVED_QUERY_LOAD_ERROR_TOAST = i18n.translate( + 'xpack.securitySolution.hooks.useGetSavedQuery.errorToastMessage', + { + defaultMessage: 'Failed to load the saved query', + } +); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_get_saved_query.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/use_get_saved_query.ts similarity index 81% rename from x-pack/plugins/security_solution/public/common/hooks/use_get_saved_query.ts rename to x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/use_get_saved_query.ts index 932fe61e2ce4..4c3c4c6c42fe 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_get_saved_query.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/use_get_saved_query.ts @@ -7,12 +7,13 @@ import { useEffect, useMemo } from 'react'; -import { useSavedQueryServices } from '../utils/saved_query_services'; -import type { DefineStepRule } from '../../detections/pages/detection_engine/rules/types'; +import { useSavedQueryServices } from '../../../../../common/utils/saved_query_services'; +import type { DefineStepRule } from '../types'; + +import { useFetch, REQUEST_NAMES } from '../../../../../common/hooks/use_fetch'; +import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; -import { useFetch, REQUEST_NAMES } from './use_fetch'; import { SAVED_QUERY_LOAD_ERROR_TOAST } from './translations'; -import { useAppToasts } from './use_app_toasts'; export const useGetSavedQuery = (savedQueryId: string | undefined) => { const savedQueryServices = useSavedQueryServices(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts index bc22ca9ea44d..521fdf1e5a59 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts @@ -97,13 +97,10 @@ export const getFilter = async ({ index, exceptionFilter, }); - } else if (savedId && index != null) { - // if savedId present and we ending up here, then saved query failed to be fetched - // and we also didn't fall back to saved in rule query - throw Error(`Failed to fetch saved query. "${err.message}"`); } else { // user did not give any additional fall back mechanism for generating a rule // rethrow error for activity monitoring + err.message = `Failed to fetch saved query. "${err.message}"`; throw err; } } From ce35fcc26861e1c9e2dd8661f6742a2f119fb2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 27 Sep 2022 17:15:45 +0200 Subject: [PATCH 024/185] [Security Solution][Endpoint] Fix react minified error when getting endpoint data (#141212) * Fix react minified error when getting enpoint data * Revert overflow change to hidden Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/confirm_incoming_data_with_preview.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx index af0e3c882ea5..011134151cbd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx @@ -75,7 +75,8 @@ const HitPreview: React.FC<{ hit: SearchHit }> = ({ hit }) => { ); const listItems = Object.entries(hitForDisplay).map(([key, value]) => ({ title: `${key}:`, - description: value, + // Ensures arrays and collections of nested objects are displayed correctly + description: JSON.stringify(value), })); return ( From f567c953d54cbaac2e738377f222b48e31f4afb6 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Tue, 27 Sep 2022 11:00:27 -0500 Subject: [PATCH 025/185] [Enterprise Search] align ml inference processor state (#141720) * align ml inference processor state updated how we render the state of the ml inference processor state to be inline with the trained model state. This will ensure we give the user more information if the model is in a state that will cause an error. * [Enterprise Search] replace flags with enum for model state Updated the InferencePipeline type to use an enum for the modelState instead of passing the string from the model and this also replaces the `isDeployed` flags since we want a more granular state that the enum will provide. --- .../common/types/pipelines.ts | 11 +- .../inference_pipeline_card.test.tsx | 14 +- .../pipelines/inference_pipeline_card.tsx | 42 +++-- .../pipelines/ml_model_health.test.tsx | 81 ++++++++++ .../pipelines/ml_model_health.tsx | 144 ++++++++++++++++++ .../enterprise_search_content/routes.ts | 2 + ...h_ml_inference_pipeline_processors.test.ts | 106 ++++++++++--- .../fetch_ml_inference_pipeline_processors.ts | 34 ++++- 8 files changed, 381 insertions(+), 53 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.tsx diff --git a/x-pack/plugins/enterprise_search/common/types/pipelines.ts b/x-pack/plugins/enterprise_search/common/types/pipelines.ts index 60fd87ca523b..269f7149cc7b 100644 --- a/x-pack/plugins/enterprise_search/common/types/pipelines.ts +++ b/x-pack/plugins/enterprise_search/common/types/pipelines.ts @@ -6,7 +6,16 @@ */ export interface InferencePipeline { - isDeployed: boolean; + modelState: TrainedModelState; + modelStateReason?: string; pipelineName: string; types: string[]; } + +export enum TrainedModelState { + NotDeployed = '', + Starting = 'starting', + Stopping = 'stopping', + Started = 'started', + Failed = 'failed', +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx index 1c79cff0244e..27dc055564bd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx @@ -11,14 +11,16 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiBadge, EuiHealth, EuiPanel, EuiTitle } from '@elastic/eui'; +import { EuiBadge, EuiPanel, EuiTitle } from '@elastic/eui'; + +import { InferencePipeline, TrainedModelState } from '../../../../../../common/types/pipelines'; import { InferencePipelineCard } from './inference_pipeline_card'; +import { TrainedModelHealth } from './ml_model_health'; -export const DEFAULT_VALUES = { - isDeployed: true, +export const DEFAULT_VALUES: InferencePipeline = { + modelState: TrainedModelState.Started, pipelineName: 'Sample Processor', - trainedModelName: 'example_trained_model', types: ['pytorch'], }; @@ -34,8 +36,6 @@ describe('InferencePipelineCard', () => { expect(wrapper.find(EuiPanel)).toHaveLength(1); expect(wrapper.find(EuiTitle)).toHaveLength(1); expect(wrapper.find(EuiBadge)).toHaveLength(1); - - const health = wrapper.find(EuiHealth); - expect(health.prop('children')).toEqual('Deployed'); + expect(wrapper.find(TrainedModelHealth)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx index b73121f947d7..e8017ff15a19 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx @@ -12,31 +12,30 @@ import { useActions, useValues } from 'kea'; import { EuiBadge, EuiButtonEmpty, + EuiButtonIcon, EuiConfirmModal, EuiFlexGroup, EuiFlexItem, - EuiHealth, EuiPanel, EuiPopover, EuiPopoverTitle, EuiText, EuiTitle, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { InferencePipeline } from '../../../../../../common/types/pipelines'; +import { InferencePipeline, TrainedModelState } from '../../../../../../common/types/pipelines'; import { CANCEL_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; import { HttpLogic } from '../../../../shared/http'; +import { ML_MANAGE_TRAINED_MODELS_PATH } from '../../../routes'; import { IndexNameLogic } from '../index_name_logic'; +import { TrainedModelHealth } from './ml_model_health'; import { PipelinesLogic } from './pipelines_logic'; -export const InferencePipelineCard: React.FC = ({ - pipelineName, - isDeployed, - types, -}) => { +export const InferencePipelineCard: React.FC = (pipeline) => { const { http } = useValues(HttpLogic); const { indexName } = useValues(IndexNameLogic); const [isPopOverOpen, setIsPopOverOpen] = useState(false); @@ -46,10 +45,7 @@ export const InferencePipelineCard: React.FC = ({ setShowConfirmDelete(true); setIsPopOverOpen(false); }; - - const deployedText = i18n.translate('xpack.enterpriseSearch.inferencePipelineCard.isDeployed', { - defaultMessage: 'Deployed', - }); + const { pipelineName, types } = pipeline; const actionButton = ( = ({ - - {isDeployed && ( - - {deployedText} + + + + + {pipeline.modelState === TrainedModelState.NotDeployed && ( + + + + )} {types.map((type) => ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.test.tsx new file mode 100644 index 000000000000..0eb88abb317e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiHealth } from '@elastic/eui'; + +import { InferencePipeline, TrainedModelState } from '../../../../../../common/types/pipelines'; + +import { TrainedModelHealth } from './ml_model_health'; + +describe('TrainedModelHealth', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues({}); + }); + + const commonModelData: InferencePipeline = { + modelState: TrainedModelState.NotDeployed, + pipelineName: 'Sample Processor', + types: ['pytorch'], + }; + it('renders model started', () => { + const pipeline: InferencePipeline = { + ...commonModelData, + modelState: TrainedModelState.Started, + }; + const wrapper = shallow(); + const health = wrapper.find(EuiHealth); + expect(health.prop('children')).toEqual('Started'); + expect(health.prop('color')).toEqual('success'); + }); + it('renders model not deployed', () => { + const pipeline: InferencePipeline = { + ...commonModelData, + }; + const wrapper = shallow(); + const health = wrapper.find(EuiHealth); + expect(health.prop('children')).toEqual('Not deployed'); + expect(health.prop('color')).toEqual('danger'); + }); + it('renders model stopping', () => { + const pipeline: InferencePipeline = { + ...commonModelData, + modelState: TrainedModelState.Stopping, + }; + const wrapper = shallow(); + const health = wrapper.find(EuiHealth); + expect(health.prop('children')).toEqual('Stopping'); + expect(health.prop('color')).toEqual('warning'); + }); + it('renders model starting', () => { + const pipeline: InferencePipeline = { + ...commonModelData, + modelState: TrainedModelState.Starting, + }; + const wrapper = shallow(); + const health = wrapper.find(EuiHealth); + expect(health.prop('children')).toEqual('Starting'); + expect(health.prop('color')).toEqual('warning'); + }); + it('renders model failed', () => { + const pipeline: InferencePipeline = { + ...commonModelData, + modelState: TrainedModelState.Failed, + modelStateReason: 'Model start boom.', + }; + const wrapper = shallow(); + const health = wrapper.find(EuiHealth); + expect(health.prop('children')).toEqual('Deployment failed'); + expect(health.prop('color')).toEqual('danger'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.tsx new file mode 100644 index 000000000000..0d47c7018d4f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_model_health.tsx @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { EuiHealth, EuiToolTip } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { InferencePipeline, TrainedModelState } from '../../../../../../common/types/pipelines'; + +const modelStartedText = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.started', + { + defaultMessage: 'Started', + } +); +const modelStartedTooltip = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.started.tooltip', + { + defaultMessage: 'This trained model is running and fully available', + } +); +const modelStartingText = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.starting', + { + defaultMessage: 'Starting', + } +); +const modelStartingTooltip = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.starting.tooltip', + { + defaultMessage: + 'This trained model is in the process of starting up and will be available shortly', + } +); +const modelStoppingText = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.stopping', + { + defaultMessage: 'Stopping', + } +); +const modelStoppingTooltip = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.stopping.tooltip', + { + defaultMessage: + 'This trained model is in the process of shutting down and is currently unavailable', + } +); +const modelDeploymentFailedText = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.deploymentFailed', + { + defaultMessage: 'Deployment failed', + } +); +const modelNotDeployedText = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.notDeployed', + { + defaultMessage: 'Not deployed', + } +); +const modelNotDeployedTooltip = i18n.translate( + 'xpack.enterpriseSearch.inferencePipelineCard.modelState.notDeployed.tooltip', + { + defaultMessage: + 'This trained model is not currently deployed. Visit the trained models page to make changes', + } +); + +export const TrainedModelHealth: React.FC = ({ + modelState, + modelStateReason, +}) => { + let modelHealth: { + healthColor: string; + healthText: React.ReactNode; + tooltipText: React.ReactNode; + }; + switch (modelState) { + case TrainedModelState.Started: + modelHealth = { + healthColor: 'success', + healthText: modelStartedText, + tooltipText: modelStartedTooltip, + }; + break; + case TrainedModelState.Stopping: + modelHealth = { + healthColor: 'warning', + healthText: modelStoppingText, + tooltipText: modelStoppingTooltip, + }; + break; + case TrainedModelState.Starting: + modelHealth = { + healthColor: 'warning', + healthText: modelStartingText, + tooltipText: modelStartingTooltip, + }; + break; + case TrainedModelState.Failed: + modelHealth = { + healthColor: 'danger', + healthText: modelDeploymentFailedText, + tooltipText: ( + + ), + }; + break; + case TrainedModelState.NotDeployed: + modelHealth = { + healthColor: 'danger', + healthText: modelNotDeployedText, + tooltipText: modelNotDeployedTooltip, + }; + break; + } + return ( + + {modelHealth.healthText} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts index 60260dcaa537..a98040211906 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts @@ -21,3 +21,5 @@ export const SEARCH_INDEX_PATH = `${SEARCH_INDICES_PATH}/:indexName`; export const SEARCH_INDEX_TAB_PATH = `${SEARCH_INDEX_PATH}/:tabId`; export const SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/crawler/domains/:domainId`; export const SEARCH_INDEX_SELECT_CONNECTOR_PATH = `${SEARCH_INDEX_PATH}/select_connector`; + +export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models'; diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts index 9a34bb42ec87..79d4600bb31e 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts @@ -9,7 +9,7 @@ import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBo import { ElasticsearchClient } from '@kbn/core/server'; import { BUILT_IN_MODEL_TAG } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; -import { InferencePipeline } from '../../../common/types/pipelines'; +import { InferencePipeline, TrainedModelState } from '../../../common/types/pipelines'; import { fetchAndAddTrainedModelData, @@ -169,35 +169,74 @@ const mockGetTrainedModelsData = { model_type: 'pytorch', tags: [], }, + { + inference_config: { text_classification: {} }, + model_id: 'trained-model-id-3', + model_type: 'pytorch', + tags: [], + }, + { + inference_config: { fill_mask: {} }, + model_id: 'trained-model-id-4', + model_type: 'pytorch', + tags: [], + }, ], }; const mockGetTrainedModelStats = { - count: 1, + count: 4, trained_model_stats: [ { model_id: 'trained-model-id-1', }, { deployment_stats: { + allocation_status: { + allocation_count: 1, + }, state: 'started', }, model_id: 'trained-model-id-2', }, + { + deployment_stats: { + allocation_status: { + allocation_count: 1, + }, + state: 'failed', + reason: 'something is wrong, boom', + }, + model_id: 'trained-model-id-3', + }, + { + deployment_stats: { + allocation_status: { + allocation_count: 1, + }, + state: 'starting', + }, + model_id: 'trained-model-id-4', + }, ], }; -const trainedModelDataObject = { +const trainedModelDataObject: Record = { 'trained-model-id-1': { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-1', types: ['lang_ident', 'ner'], }, 'trained-model-id-2': { - isDeployed: true, + modelState: TrainedModelState.Started, pipelineName: 'ml-inference-pipeline-2', types: ['pytorch', 'ner'], }, + 'ml-inference-pipeline-3': { + modelState: TrainedModelState.NotDeployed, + pipelineName: 'ml-inference-pipeline-3', + types: ['lang_ident', 'ner'], + }, }; describe('fetchMlInferencePipelineProcessorNames lib function', () => { @@ -254,15 +293,15 @@ describe('fetchPipelineProcessorInferenceData lib function', () => { it('should return the inference processor data for the pipelines', async () => { mockClient.ingest.getPipeline.mockImplementation(() => Promise.resolve(mockGetPipeline2)); - const expected = [ + const expected: InferencePipelineData[] = [ { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-1', trainedModelName: 'trained-model-id-1', types: [], }, { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-2', trainedModelName: 'trained-model-id-2', types: [], @@ -338,20 +377,20 @@ describe('getMlModelConfigsForModelIds lib function', () => { Promise.resolve(mockGetTrainedModelStats) ); - const input = { + const input: Record = { 'trained-model-id-1': { - isDeployed: true, + modelState: TrainedModelState.Started, pipelineName: '', trainedModelName: 'trained-model-id-1', types: ['pytorch', 'ner'], }, 'trained-model-id-2': { - isDeployed: true, + modelState: TrainedModelState.Started, pipelineName: '', trainedModelName: 'trained-model-id-2', types: ['pytorch', 'ner'], }, - } as Record; + }; const expected = { 'trained-model-id-2': input['trained-model-id-2'], @@ -392,32 +431,57 @@ describe('fetchAndAddTrainedModelData lib function', () => { const pipelines: InferencePipelineData[] = [ { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-1', trainedModelName: 'trained-model-id-1', types: [], }, { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-2', trainedModelName: 'trained-model-id-2', types: [], }, + { + modelState: TrainedModelState.NotDeployed, + pipelineName: 'ml-inference-pipeline-3', + trainedModelName: 'trained-model-id-3', + types: [], + }, + { + modelState: TrainedModelState.NotDeployed, + pipelineName: 'ml-inference-pipeline-4', + trainedModelName: 'trained-model-id-4', + types: [], + }, ]; const expected: InferencePipelineData[] = [ { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: 'ml-inference-pipeline-1', trainedModelName: 'trained-model-id-1', types: ['lang_ident', 'ner'], }, { - isDeployed: true, + modelState: TrainedModelState.Started, pipelineName: 'ml-inference-pipeline-2', trainedModelName: 'trained-model-id-2', types: ['pytorch', 'ner'], }, + { + modelState: TrainedModelState.Failed, + modelStateReason: 'something is wrong, boom', + pipelineName: 'ml-inference-pipeline-3', + trainedModelName: 'trained-model-id-3', + types: ['pytorch', 'text_classification'], + }, + { + modelState: TrainedModelState.Starting, + pipelineName: 'ml-inference-pipeline-4', + trainedModelName: 'trained-model-id-4', + types: ['pytorch', 'fill_mask'], + }, ]; const response = await fetchAndAddTrainedModelData( @@ -426,10 +490,10 @@ describe('fetchAndAddTrainedModelData lib function', () => { ); expect(mockClient.ml.getTrainedModels).toHaveBeenCalledWith({ - model_id: 'trained-model-id-1,trained-model-id-2', + model_id: 'trained-model-id-1,trained-model-id-2,trained-model-id-3,trained-model-id-4', }); expect(mockClient.ml.getTrainedModelsStats).toHaveBeenCalledWith({ - model_id: 'trained-model-id-1,trained-model-id-2', + model_id: 'trained-model-id-1,trained-model-id-2,trained-model-id-3,trained-model-id-4', }); expect(response).toEqual(expected); }); @@ -551,11 +615,7 @@ describe('fetchMlInferencePipelineProcessors lib function', () => { const expected: InferencePipeline[] = [ trainedModelDataObject['trained-model-id-1'], - { - isDeployed: false, - pipelineName: 'ml-inference-pipeline-3', - types: ['lang_ident', 'ner'], - }, + trainedModelDataObject['ml-inference-pipeline-3'], ]; const response = await fetchMlInferencePipelineProcessors( diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts index 3b695d53ba9a..95839a9b6ac2 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts @@ -9,7 +9,7 @@ import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBo import { ElasticsearchClient } from '@kbn/core/server'; import { BUILT_IN_MODEL_TAG } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; -import { InferencePipeline } from '../../../common/types/pipelines'; +import { InferencePipeline, TrainedModelState } from '../../../common/types/pipelines'; import { getInferencePipelineNameFromIndexName } from '../../utils/ml_inference_pipeline_utils'; export type InferencePipelineData = InferencePipeline & { @@ -58,7 +58,7 @@ export const fetchPipelineProcessorInferenceData = async ( const trainedModelName = inferenceProcessor?.inference?.model_id; if (trainedModelName) pipelineProcessorData.push({ - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: pipelineProcessorName, trainedModelName, types: [], @@ -98,7 +98,7 @@ export const getMlModelConfigsForModelIds = async ( if (trainedModelNames.includes(trainedModelName)) { modelConfigs[trainedModelName] = { - isDeployed: false, + modelState: TrainedModelState.NotDeployed, pipelineName: '', trainedModelName, types: getMlModelTypesForModelConfig(trainedModelData), @@ -109,8 +109,27 @@ export const getMlModelConfigsForModelIds = async ( trainedModelsStats.trained_model_stats.forEach((trainedModelStats) => { const trainedModelName = trainedModelStats.model_id; if (modelConfigs.hasOwnProperty(trainedModelName)) { - const isDeployed = trainedModelStats.deployment_stats?.state === 'started'; - modelConfigs[trainedModelName].isDeployed = isDeployed; + let modelState: TrainedModelState; + switch (trainedModelStats.deployment_stats?.state) { + case 'started': + modelState = TrainedModelState.Started; + break; + case 'starting': + modelState = TrainedModelState.Starting; + break; + case 'stopping': + modelState = TrainedModelState.Stopping; + break; + // @ts-ignore: type is wrong, "failed" is a possible state + case 'failed': + modelState = TrainedModelState.Failed; + break; + default: + modelState = TrainedModelState.NotDeployed; + break; + } + modelConfigs[trainedModelName].modelState = modelState; + modelConfigs[trainedModelName].modelStateReason = trainedModelStats.deployment_stats?.reason; } }); @@ -131,11 +150,12 @@ export const fetchAndAddTrainedModelData = async ( if (!model) { return data; } - const { types, isDeployed } = model; + const { types, modelState, modelStateReason } = model; return { ...data, types, - isDeployed, + modelState, + modelStateReason, }; }); }; From db931bf60e1a639dc887cdcae8239c96c6a098bf Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 27 Sep 2022 18:03:40 +0200 Subject: [PATCH 026/185] Update renovate.json with new backport labels (#141954) Use `backport:all-open` instead of the deprecated `auto-backport` label. --- renovate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 6eed31366c40..4075b2452bea 100644 --- a/renovate.json +++ b/renovate.json @@ -125,7 +125,7 @@ ], "reviewers": ["team:kibana-security"], "matchBaseBranches": ["main"], - "labels": ["Team:Security", "release_note:skip", "auto-backport"], + "labels": ["Team:Security", "release_note:skip", "backport:all-open"], "enabled": true }, { From bde62fd5d82deb56d47fa99f8112e030c62a45de Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 27 Sep 2022 18:22:22 +0200 Subject: [PATCH 027/185] Fix wrong Critical Hosts and Critical Users count color (#141953) --- .../overview/components/entity_analytics/header/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx index d8f087a7cc88..c1a52364a76f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx @@ -28,7 +28,7 @@ import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml import { useQueryInspector } from '../../../../common/components/page/manage_query'; const StyledEuiTitle = styled(EuiTitle)` - color: ${({ theme: { eui } }) => eui.euiColorVis9}; + color: ${({ theme: { eui } }) => eui.euiColorDanger}; `; const HOST_RISK_QUERY_ID = 'hostRiskScoreKpiQuery'; From 7aa54285975814e0b4cdb5f6344e22b470a91383 Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Tue, 27 Sep 2022 18:29:34 +0200 Subject: [PATCH 028/185] [Security Solution] Don't mute rules when bulk editing rule actions (#140626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Intro This PR modifies the logic of bulk updating rule actions, in preparation for https://github.com/elastic/kibana/pull/137430 ## Summary - Removes the mute logic for bulk updating rule actions - Remove option for “Perform no actions” from the bulk update rule actions dropdown options ONLY (option still available when creating or editing rules individually) - Also corrects bulk update rule actions flyout, so that: - available actions are always displayed - copy referring to using "Perform No Actions" to mute all selected rules is no longer displayed. ## Screenshots **Removed unwanted copy and "On each rule execution" selected as default** ![image](https://user-images.githubusercontent.com/5354282/191498419-10299ee5-4a9e-474e-b00a-657dc90816fa.png) **"Perform No Action" option no longer available** ![image](https://user-images.githubusercontent.com/5354282/191498500-3965edad-8142-4834-808e-c210e72e17cb.png) ### Checklist Delete any items that are not applicable to this PR. - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../index.ts | 1 - .../src/default_throttle_null/index.test.ts | 44 --- .../src/default_throttle_null/index.ts | 23 -- .../src/throttle/index.ts | 10 +- .../src/time_duration/index.test.ts | 373 +++++++++++++----- .../src/time_duration/index.ts | 76 ++-- .../request/perform_bulk_action_schema.ts | 19 +- .../description_step/throttle_description.tsx | 7 +- .../rules/step_rule_actions/index.tsx | 11 +- .../rules/throttle_select_field/index.tsx | 10 +- .../bulk_actions/forms/rule_actions_form.tsx | 64 +-- .../detection_engine/rules/bulk_edit_rules.ts | 33 +- .../group10/perform_bulk_action.ts | 16 +- .../group10/update_rules.ts | 4 +- .../group10/update_rules_bulk.ts | 6 +- 15 files changed, 412 insertions(+), 285 deletions(-) delete mode 100644 packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts delete mode 100644 packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/index.ts index c2e00482cd2d..75d5352c9f63 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/index.ts @@ -18,7 +18,6 @@ export * from './src/default_per_page'; export * from './src/default_risk_score_mapping_array'; export * from './src/default_severity_mapping_array'; export * from './src/default_threat_array'; -export * from './src/default_throttle_null'; export * from './src/default_to_string'; export * from './src/default_uuid'; export * from './src/from'; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts deleted file mode 100644 index b92815d4fe82..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts +++ /dev/null @@ -1,44 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { Throttle } from '../throttle'; -import { DefaultThrottleNull } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_throttle_null', () => { - test('it should validate a throttle string', () => { - const payload: Throttle = 'some string'; - const decoded = DefaultThrottleNull.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate an array with a number', () => { - const payload = 5; - const decoded = DefaultThrottleNull.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultThreatNull"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default "null" if not provided a value', () => { - const payload = undefined; - const decoded = DefaultThrottleNull.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(null); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts deleted file mode 100644 index e9b9ec27ea41..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { throttle, ThrottleOrNull } from '../throttle'; - -/** - * Types the DefaultThrottleNull as: - * - If null or undefined, then a null will be set - */ -export const DefaultThrottleNull = new t.Type( - 'DefaultThreatNull', - throttle.is, - (input, context): Either => - input == null ? t.success(null) : throttle.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts index 19e75dcc3be0..d7d636ad0994 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts @@ -6,13 +6,15 @@ * Side Public License, v 1. */ +import { TimeDuration } from '@kbn/securitysolution-io-ts-types'; import * as t from 'io-ts'; -export const throttle = t.string; +export const throttle = t.union([ + t.literal('no_actions'), + t.literal('rule'), + TimeDuration({ allowedUnits: ['h', 'd'] }), +]); export type Throttle = t.TypeOf; export const throttleOrNull = t.union([throttle, t.null]); export type ThrottleOrNull = t.TypeOf; - -export const throttleOrNullOrUndefined = t.union([throttle, t.null, t.undefined]); -export type ThrottleOrUndefinedOrNull = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts index 872affa9989a..dc6b5e61ebbf 100644 --- a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts +++ b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts @@ -6,122 +6,315 @@ * Side Public License, v 1. */ -import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; import { TimeDuration } from '.'; import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; describe('TimeDuration', () => { - test('it should validate a correctly formed TimeDuration with time unit of seconds', () => { - const payload = '1s'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + describe('with allowedDurations', () => { + test('it should validate a correctly formed TimeDuration with an allowed duration of 1s', () => { + const payload = '1s'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 's'], + [2, 'h'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); - test('it should validate a correctly formed TimeDuration with time unit of minutes', () => { - const payload = '100m'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should validate a correctly formed TimeDuration with an allowed duration of 7d', () => { + const payload = '1s'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 's'], + [2, 'h'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); - test('it should validate a correctly formed TimeDuration with time unit of hours', () => { - const payload = '10000000h'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate a time duration if the allowed durations does not include it', () => { + const payload = '24h'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 's'], + [2, 'h'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "24h" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate a TimeDuration of 0 length', () => { - const payload = '0s'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate a an allowed duration with a negative number', () => { + const payload = '10s'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 's'], + [-7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0s" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[[1,"s"],[-7,"d"]]" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate a negative TimeDuration', () => { - const payload = '-10s'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate an allowed duration with a fractional number', () => { + const payload = '1.5s'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 's'], + [-7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-10s" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1.5s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate a decimal TimeDuration', () => { - const payload = '1.5s'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate a an allowed duration with a duration of 0', () => { + const payload = '10s'; + const decoded = TimeDuration({ + allowedDurations: [ + [0, 's'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1.5s" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[[0,"s"],[7,"d"]]" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate a TimeDuration with some other time unit', () => { - const payload = '10000000w'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate a TimeDuration with an invalid time unit', () => { + const payload = '10000000days'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "10000000w" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "10000000days" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => { - const payload = '100ff0000w'; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => { + const payload = '100ff0000w'; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "100ff0000w" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "100ff0000w" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate an empty string', () => { - const payload = ''; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate an empty string', () => { + const payload = ''; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "TimeDuration"']); - expect(message.schema).toEqual({}); - }); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate an number', () => { + const payload = 100; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); - test('it should NOT validate an number', () => { - const payload = 100; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "100" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "100" supplied to "TimeDuration"', - ]); - expect(message.schema).toEqual({}); + test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => { + const payload = `${Math.pow(2, 53)}h`; + const decoded = TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + `Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`, + ]); + expect(message.schema).toEqual({}); + }); }); + describe('with allowedUnits', () => { + test('it should validate a correctly formed TimeDuration with time unit of seconds', () => { + const payload = '1s'; + const decoded = TimeDuration({ allowedUnits: ['s'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of minutes', () => { + const payload = '100m'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of hours', () => { + const payload = '10000000h'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of days', () => { + const payload = '7d'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a correctly formed TimeDuration with time unit of seconds if it is not an allowed unit', () => { + const payload = '30s'; + const decoded = TimeDuration({ allowedUnits: ['m', 'h', 'd'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "30s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a negative TimeDuration', () => { + const payload = '-10s'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-10s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a fractional number', () => { + const payload = '1.5s'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1.5s" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a TimeDuration with an invalid time unit', () => { + const payload = '10000000days'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "10000000days" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => { + const payload = '100ff0000w'; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "100ff0000w" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate an empty string', () => { + const payload = ''; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate an number', () => { + const payload = 100; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload); + const message = foldLeftRight(decoded); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "100" supplied to "TimeDuration"', + ]); + expect(message.schema).toEqual({}); + }); - test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => { - const payload = `${Math.pow(2, 53)}h`; - const decoded = TimeDuration.decode(payload); - const message = pipe(decoded, foldLeftRight); + test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => { + const payload = `${Math.pow(2, 53)}h`; + const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload); + const message = foldLeftRight(decoded); - expect(getPaths(left(message.errors))).toEqual([ - `Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`, - ]); - expect(message.schema).toEqual({}); + expect(getPaths(left(message.errors))).toEqual([ + `Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`, + ]); + expect(message.schema).toEqual({}); + }); }); }); diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts index 5549979ac68d..0b69c62e0d2f 100644 --- a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts +++ b/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts @@ -12,38 +12,60 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the TimeDuration as: * - A string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time - * - in the format {safe_integer}{timeUnit}, e.g. "30s", "1m", "2h" + * - in the format {safe_integer}{timeUnit}, e.g. "30s", "1m", "2h", "7d" */ -export const TimeDuration = new t.Type( - 'TimeDuration', - t.string.is, - (input, context): Either => { - if (typeof input === 'string' && input.trim() !== '') { - try { - const inputLength = input.length; - const unit = input.trim().at(-1); - const time = parseFloat(input.trim().substring(0, inputLength - 1)); - if (!Number.isInteger(time)) { - return t.failure(input, context); - } - if ( - time >= 1 && - Number.isSafeInteger(time) && - (unit === 's' || unit === 'm' || unit === 'h') - ) { - return t.success(input); - } else { +type TimeUnits = 's' | 'm' | 'h' | 'd' | 'w' | 'y'; +interface TimeDurationWithAllowedDurations { + allowedDurations: Array<[number, TimeUnits]>; + allowedUnits?: never; +} +interface TimeDurationWithAllowedUnits { + allowedUnits: TimeUnits[]; + allowedDurations?: never; +} + +type TimeDurationType = TimeDurationWithAllowedDurations | TimeDurationWithAllowedUnits; + +const isTimeSafe = (time: number) => time >= 1 && Number.isSafeInteger(time); + +export const TimeDuration = ({ allowedUnits, allowedDurations }: TimeDurationType) => { + return new t.Type( + 'TimeDuration', + t.string.is, + (input, context): Either => { + if (typeof input === 'string' && input.trim() !== '') { + try { + const inputLength = input.length; + const time = Number(input.trim().substring(0, inputLength - 1)); + const unit = input.trim().at(-1); + if (!isTimeSafe(time)) { + return t.failure(input, context); + } + if (allowedDurations) { + for (const [allowedTime, allowedUnit] of allowedDurations) { + if (!isTimeSafe(allowedTime)) { + return t.failure(allowedDurations, context); + } + if (allowedTime === time && allowedUnit === unit) { + return t.success(input); + } + } + return t.failure(input, context); + } else if (allowedUnits.includes(unit as TimeUnits)) { + return t.success(input); + } else { + return t.failure(input, context); + } + } catch (error) { return t.failure(input, context); } - } catch (error) { + } else { return t.failure(input, context); } - } else { - return t.failure(input, context); - } - }, - t.identity -); + }, + t.identity + ); +}; export type TimeDurationC = typeof TimeDuration; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts index a74845721a02..0140e5f8d926 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/perform_bulk_action_schema.ts @@ -9,7 +9,6 @@ import * as t from 'io-ts'; import { NonEmptyArray, TimeDuration, enumeration } from '@kbn/securitysolution-io-ts-types'; import { - throttle, action_group as actionGroup, action_params as actionParams, action_id as actionId, @@ -41,6 +40,18 @@ export enum BulkActionEditType { 'set_schedule' = 'set_schedule', } +export const throttleForBulkActions = t.union([ + t.literal('rule'), + TimeDuration({ + allowedDurations: [ + [1, 'h'], + [1, 'd'], + [7, 'd'], + ], + }), +]); +export type ThrottleForBulkActions = t.TypeOf; + const bulkActionEditPayloadTags = t.type({ type: t.union([ t.literal(BulkActionEditType.add_tags), @@ -96,7 +107,7 @@ const bulkActionEditPayloadRuleActions = t.type({ t.literal(BulkActionEditType.set_rule_actions), ]), value: t.type({ - throttle, + throttle: throttleForBulkActions, actions: t.array(normalizedRuleAction), }), }); @@ -106,8 +117,8 @@ export type BulkActionEditPayloadRuleActions = t.TypeOf; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/throttle_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/throttle_description.tsx index 69110b2e5360..c0b6780ea8bc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/throttle_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/throttle_description.tsx @@ -6,10 +6,13 @@ */ import { find } from 'lodash/fp'; -import { THROTTLE_OPTIONS, DEFAULT_THROTTLE_OPTION } from '../throttle_select_field'; +import { + THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, + DEFAULT_THROTTLE_OPTION, +} from '../throttle_select_field'; export const buildThrottleDescription = (value = DEFAULT_THROTTLE_OPTION.value, title: string) => { - const throttleOption = find(['value', value], THROTTLE_OPTIONS); + const throttleOption = find(['value', value], THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING); return { title, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 5cdf89df572c..fe2c5d8fa4ee 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -33,7 +33,7 @@ import { Form, UseField, useForm, useFormData } from '../../../../shared_imports import { StepContentWrapper } from '../step_content_wrapper'; import { ThrottleSelectField, - THROTTLE_OPTIONS, + THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, DEFAULT_THROTTLE_OPTION, } from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; @@ -62,11 +62,14 @@ const GhostFormField = () => <>; const getThrottleOptions = (throttle?: string | null) => { // Add support for throttle options set by the API - if (throttle && findIndex(['value', throttle], THROTTLE_OPTIONS) < 0) { - return [...THROTTLE_OPTIONS, { value: throttle, text: throttle }]; + if ( + throttle && + findIndex(['value', throttle], THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING) < 0 + ) { + return [...THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, { value: throttle, text: throttle }]; } - return THROTTLE_OPTIONS; + return THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING; }; const DisplayActionsHeader = () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/throttle_select_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/throttle_select_field/index.tsx index 4bc15d418a38..264e72fafb60 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/throttle_select_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/throttle_select_field/index.tsx @@ -13,15 +13,19 @@ import { } from '../../../../../common/constants'; import { SelectField } from '../../../../shared_imports'; -export const THROTTLE_OPTIONS = [ - { value: NOTIFICATION_THROTTLE_NO_ACTIONS, text: 'Perform no actions' }, +export const THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS = [ { value: NOTIFICATION_THROTTLE_RULE, text: 'On each rule execution' }, { value: '1h', text: 'Hourly' }, { value: '1d', text: 'Daily' }, { value: '7d', text: 'Weekly' }, ]; -export const DEFAULT_THROTTLE_OPTION = THROTTLE_OPTIONS[0]; +export const THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING = [ + { value: NOTIFICATION_THROTTLE_NO_ACTIONS, text: 'Perform no actions' }, + ...THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, +]; + +export const DEFAULT_THROTTLE_OPTION = THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING[0]; type ThrottleSelectField = typeof SelectField; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx index 8a9e52c9d552..0ec448a1ab1f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/rule_actions_form.tsx @@ -12,6 +12,7 @@ import type { RuleAction, ActionTypeRegistryContract, } from '@kbn/triggers-actions-ui-plugin/public'; + import type { FormSchema } from '../../../../../../../shared_imports'; import { useForm, @@ -21,9 +22,12 @@ import { getUseField, Field, } from '../../../../../../../shared_imports'; -import type { BulkActionEditPayload } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; import { BulkActionEditType } from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../../../common/constants'; +import type { + BulkActionEditPayload, + ThrottleForBulkActions, +} from '../../../../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; +import { NOTIFICATION_THROTTLE_RULE } from '../../../../../../../../common/constants'; import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; import { bulkAddRuleActions as i18n } from '../translations'; @@ -32,7 +36,7 @@ import { useKibana } from '../../../../../../../common/lib/kibana'; import { ThrottleSelectField, - THROTTLE_OPTIONS, + THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, } from '../../../../../../components/rules/throttle_select_field'; import { getAllActionMessageParams } from '../../../helpers'; @@ -42,7 +46,7 @@ import { validateRuleActionsField } from '../../../../../../containers/detection const CommonUseField = getUseField({ component: Field }); export interface RuleActionsFormData { - throttle: string; + throttle: ThrottleForBulkActions; actions: RuleAction[]; overwrite: boolean; } @@ -68,7 +72,7 @@ const getFormSchema = ( }); const defaultFormData: RuleActionsFormData = { - throttle: NOTIFICATION_THROTTLE_NO_ACTIONS, + throttle: NOTIFICATION_THROTTLE_RULE, actions: [], overwrite: false, }; @@ -93,7 +97,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction defaultValue: defaultFormData, }); - const [{ overwrite, throttle }] = useFormData({ form, watch: ['overwrite', 'throttle'] }); + const [{ overwrite }] = useFormData({ form, watch: ['overwrite', 'throttle'] }); const handleSubmit = useCallback(async () => { const { data, isValid } = await form.submit(); @@ -121,7 +125,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction dataTestSubj: 'bulkEditRulesRuleActionThrottle', hasNoInitialSelection: false, euiFieldProps: { - options: THROTTLE_OPTIONS, + options: THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, }, }), [] @@ -129,8 +133,6 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction const messageVariables = useMemo(() => getAllActionMessageParams(), []); - const showActionsSelect = throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS; - return ( -
  • - - - - ), - overwriteActionsCheckbox: ( - - - - ), - }} - /> -
  • {i18n.RULE_VARIABLES_DETAIL}
  • @@ -193,18 +171,14 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction /> - {showActionsSelect && ( - <> - - - - )} + + [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].includes(rule.type) ); - // bulk edit actions are applying in a historical order. + // bulk edit actions are applied in historical order. // So, we need to find a rule action that will be applied the last, to be able to check if rule should be muted/unmuted const rulesAction = ruleActions.pop(); if (rulesAction) { - const muteOrUnmuteErrors: BulkEditError[] = []; - const rulesToMuteOrUnmute = await pMap( + const unmuteErrors: BulkEditError[] = []; + const rulesToUnmute = await pMap( result.rules, async (rule) => { try { if (rule.muteAll && rulesAction.value.throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS) { await rulesClient.unmuteAll({ id: rule.id }); return (await readRules({ rulesClient, id: rule.id, ruleId: undefined })) ?? rule; - } else if ( - !rule.muteAll && - rulesAction.value.throttle === NOTIFICATION_THROTTLE_NO_ACTIONS - ) { - await rulesClient.muteAll({ id: rule.id }); - return (await readRules({ rulesClient, id: rule.id, ruleId: undefined })) ?? rule; } return rule; } catch (err) { - muteOrUnmuteErrors.push({ + unmuteErrors.push({ message: err.message, rule: { id: rule.id, @@ -115,8 +106,8 @@ export const bulkEditRules = async ({ return { ...result, - rules: rulesToMuteOrUnmute.filter((rule): rule is RuleAlertType => rule != null), - errors: [...result.errors, ...muteOrUnmuteErrors], + rules: rulesToUnmute.filter((rule): rule is RuleAlertType => rule != null), + errors: [...result.errors, ...unmuteErrors], }; } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index b60ce575c4cd..c17e679b6be3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -1513,10 +1513,9 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('throttle', () => { + // For bulk editing of rule actions, NOTIFICATION_THROTTLE_NO_ACTIONS + // is not available as payload, because "Perform No Actions" is not a valid option const casesForEmptyActions = [ - { - payloadThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, - }, { payloadThrottle: NOTIFICATION_THROTTLE_RULE, }, @@ -1561,10 +1560,6 @@ export default ({ getService }: FtrProviderContext): void => { }); const casesForNonEmptyActions = [ - { - payloadThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, - expectedThrottle: NOTIFICATION_THROTTLE_NO_ACTIONS, - }, { payloadThrottle: NOTIFICATION_THROTTLE_RULE, expectedThrottle: NOTIFICATION_THROTTLE_RULE, @@ -1616,12 +1611,9 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('notifyWhen', () => { + // For bulk editing of rule actions, NOTIFICATION_THROTTLE_NO_ACTIONS + // is not available as payload, because "Perform No Actions" is not a valid option const cases = [ - { - payload: { throttle: NOTIFICATION_THROTTLE_NO_ACTIONS }, - // keeps existing default value which is onActiveAlert - expected: { notifyWhen: 'onActiveAlert' }, - }, { payload: { throttle: '1d' }, expected: { notifyWhen: 'onThrottleInterval' }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index a8dc735376f6..705714ec3ba5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -220,7 +220,7 @@ export default ({ getService }: FtrProviderContext) => { updatedRule.rule_id = createRuleBody.rule_id; updatedRule.name = 'some other name'; updatedRule.actions = [action1]; - updatedRule.throttle = '1m'; + updatedRule.throttle = '1d'; delete updatedRule.id; const { body } = await supertest @@ -243,7 +243,7 @@ export default ({ getService }: FtrProviderContext) => { }, }, ]; - outputRule.throttle = '1m'; + outputRule.throttle = '1d'; const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body); expect(bodyToCompare).to.eql(outputRule); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index dc7209b9f1c9..2f5fb6338255 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -150,12 +150,12 @@ export default ({ getService }: FtrProviderContext) => { const updatedRule1 = getSimpleRuleUpdate('rule-1'); updatedRule1.name = 'some other name'; updatedRule1.actions = [action1]; - updatedRule1.throttle = '1m'; + updatedRule1.throttle = '1d'; const updatedRule2 = getSimpleRuleUpdate('rule-2'); updatedRule2.name = 'some other name'; updatedRule2.actions = [action1]; - updatedRule2.throttle = '1m'; + updatedRule2.throttle = '1d'; // update both rule names const { body }: { body: FullResponseSchema[] } = await supertest @@ -179,7 +179,7 @@ export default ({ getService }: FtrProviderContext) => { }, }, ]; - outputRule.throttle = '1m'; + outputRule.throttle = '1d'; const bodyToCompare = removeServerGeneratedProperties(response); expect(bodyToCompare).to.eql(outputRule); }); From 4695634fec475d6e6c0e42c23840467f272968d6 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 27 Sep 2022 20:02:47 +0300 Subject: [PATCH 029/185] Test assignees section on upgrade (#141973) --- x-pack/test/functional_with_es_ssl/apps/cases/upgrade.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/upgrade.ts b/x-pack/test/functional_with_es_ssl/apps/cases/upgrade.ts index cbbbb90c05ab..c1e2979d313b 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/upgrade.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/upgrade.ts @@ -18,6 +18,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const supertest = getService('supertest'); const testSubjects = getService('testSubjects'); const find = getService('find'); + const toasts = getService('toasts'); const updateConnector = async (id: string, req: Record) => { const { body: connector } = await supertest @@ -74,6 +75,10 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); describe('Case view page', function () { + it('does not show any error toasters', async () => { + expect(await toasts.getToastCount()).to.be(0); + }); + it('shows the title correctly', async () => { const title = await testSubjects.find('header-page-title'); expect(await title.getVisibleText()).equal('Upgrade test in Kibana'); @@ -275,6 +280,10 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the add comment button', async () => { await testSubjects.exists('submit-comment'); }); + + it('shows the assignees section', async () => { + await testSubjects.exists('case-view-assignees'); + }); }); }); }; From cbb2e2a4c6d181b87dffafba495cc391ceeb9da7 Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Tue, 27 Sep 2022 19:20:34 +0200 Subject: [PATCH 030/185] [Security Solution][Endpoint][Response Actions] Additional actions log tests (#141447) * list handler tests - tests the logic for branching with status filter - tests request params formats for strings into string [] - tests logic for index_not_found_exception * verify that hostnames filter also shows inactive agents refs elastic/security-team/issues/4721 * verify useGetEndpointsList lists 50 agents at most Even when metadata list has more than 50 agents the `useGetEndpointsList` hook should list only 50 at most refs elastic/security-team/issues/4721 * verify useGetEndpointsList shows 10 more in list When all 50 or more agents are selected in the filter list, the hook should list 10 more agents refs elastic/security-team/issues/4721 * Add a metadata list test for hostname search refs elastic/security-team/issues/4721 * Refactor table props into a hook fixes elastic/security-team/issues/4990 * remove redundant checks review changes (@paul-tavares) --- .../components/hooks.tsx | 1 - .../response_actions_log.test.tsx | 20 + .../response_actions_log.tsx | 469 +----------------- .../use_response_actions_log_table.tsx | 441 ++++++++++++++++ .../endpoint/use_get_endpoints_list.test.ts | 125 +++++ .../routes/actions/list_handler.test.ts | 169 +++++++ .../apis/metadata.fixtures.ts | 12 +- .../apis/metadata.ts | 20 + 8 files changed, 807 insertions(+), 450 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx create mode 100644 x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index bb2c1b7761d5..34cc5a3ca0f9 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -159,7 +159,6 @@ export const getCommandKey = ( } }; -// TODO: add more filter names here export type FilterName = keyof typeof FILTER_NAMES; export const useActionsLogFilter = ({ filterName, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 30148c9643ba..556c76529633 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -393,6 +393,26 @@ describe('Response actions history', () => { expect(noTrays).toEqual([]); }); + it('should contain relevant details in each expanded row', async () => { + render(); + const { getAllByTestId } = renderResult; + + const expandButtons = getAllByTestId(`${testPrefix}-expand-button`); + expandButtons.map((button) => userEvent.click(button)); + const trays = getAllByTestId(`${testPrefix}-details-tray`); + expect(trays).toBeTruthy(); + expect(Array.from(trays[0].querySelectorAll('dt')).map((title) => title.textContent)).toEqual( + [ + 'Command placed', + 'Execution started on', + 'Execution completed', + 'Input', + 'Parameters', + 'Output:', + ] + ); + }); + it('should refresh data when autoRefresh is toggled on', async () => { render(); const { getByTestId } = renderResult; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx index 2a9362830c76..ac9a0829bb7b 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -5,112 +5,29 @@ * 2.0. */ -import { - EuiAvatar, - EuiBasicTable, - EuiButtonIcon, - EuiDescriptionList, - EuiEmptyPrompt, - EuiFacetButton, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiScreenReaderOnly, - EuiI18nNumber, - EuiText, - EuiCodeBlock, - EuiToolTip, - RIGHT_ALIGNMENT, -} from '@elastic/eui'; -import { euiStyled, css } from '@kbn/kibana-react-plugin/common'; +import { EuiBasicTable, EuiEmptyPrompt, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; -import type { HorizontalAlignment, CriteriaWithPagination } from '@elastic/eui'; +import type { CriteriaWithPagination } from '@elastic/eui'; import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import type { ResponseActions, ResponseActionStatus, } from '../../../../common/endpoint/service/response_actions/constants'; -import { getEmptyValue } from '../../../common/components/empty_value'; -import { FormattedDate } from '../../../common/components/formatted_date'; + import type { ActionListApiResponse } from '../../../../common/endpoint/types'; import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; import { ManagementEmptyStateWrapper } from '../management_empty_state_wrapper'; import { useGetEndpointActionList } from '../../hooks'; -import { OUTPUT_MESSAGES, TABLE_COLUMN_NAMES, UX_MESSAGES } from './translations'; -import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../common/constants'; +import { UX_MESSAGES } from './translations'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; import { ActionsLogFilters } from './components/actions_log_filters'; -import { - getActionStatus, - getUiCommand, - getCommandKey, - useDateRangePicker, -} from './components/hooks'; -import { StatusBadge } from './components/status_badge'; +import { getCommandKey, useDateRangePicker } from './components/hooks'; import { useActionHistoryUrlParams } from './components/use_action_history_url_params'; import { useUrlPagination } from '../../hooks/use_url_pagination'; import { ManagementPageLoader } from '../management_page_loader'; import { ActionsLogEmptyState } from './components/actions_log_empty_state'; - -const emptyValue = getEmptyValue(); - -// Truncated usernames -const StyledFacetButton = euiStyled(EuiFacetButton)` - .euiText { - margin-top: 0.38rem; - overflow-y: visible !important; - } -`; - -const customDescriptionListCss = css` - &.euiDescriptionList { - > .euiDescriptionList__title { - color: ${(props) => props.theme.eui.euiColorDarkShade}; - font-size: ${(props) => props.theme.eui.euiFontSizeXS}; - margin-top: ${(props) => props.theme.eui.euiSizeS}; - } - - > .euiDescriptionList__description { - font-weight: ${(props) => props.theme.eui.euiFontWeightSemiBold}; - margin-top: ${(props) => props.theme.eui.euiSizeS}; - } - } -`; - -const StyledDescriptionList = euiStyled(EuiDescriptionList).attrs({ - compressed: true, - type: 'column', -})` - ${customDescriptionListCss} -`; - -// output section styles -const topSpacingCss = css` - ${(props) => `${props.theme.eui.euiCodeBlockPaddingModifiers.paddingMedium} 0`} -`; -const dashedBorderCss = css` - ${(props) => `1px dashed ${props.theme.eui.euiColorDisabled}`}; -`; -const StyledDescriptionListOutput = euiStyled(EuiDescriptionList).attrs({ compressed: true })` - ${customDescriptionListCss} - dd { - margin: ${topSpacingCss}; - padding: ${topSpacingCss}; - border-top: ${dashedBorderCss}; - border-bottom: ${dashedBorderCss}; - } -`; - -// code block styles -const StyledEuiCodeBlock = euiStyled(EuiCodeBlock).attrs({ - transparentBackground: true, - paddingSize: 'none', -})` - code { - color: ${(props) => props.theme.eui.euiColorDarkShade} !important; - } -`; +import { useResponseActionsLogTable } from './use_response_actions_log_table'; export const ResponseActionsLog = memo< Pick & { @@ -131,9 +48,6 @@ export const ResponseActionsLog = memo< } = useActionHistoryUrlParams(); const getTestId = useTestIdGenerator('response-actions-list'); - const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{ - [k: ActionListApiResponse['data'][number]['id']]: React.ReactNode; - }>({}); // Used to decide if display global loader or not (only the fist time tha page loads) const [isFirstAttempt, setIsFirstAttempt] = useState(true); @@ -183,6 +97,19 @@ export const ResponseActionsLog = memo< { retry: false } ); + // total actions + const totalItemCount = useMemo(() => actionList?.total ?? 0, [actionList]); + + // table columns and expanded row state + const { itemIdToExpandedRowMap, recordRangeLabel, responseActionListColumns, tablePagination } = + useResponseActionsLogTable({ + showHostNames, + pageIndex: isFlyout ? (queryParams.page || 1) - 1 : paginationFromUrlParams.page - 1, + pageSize: isFlyout ? queryParams.pageSize || 10 : paginationFromUrlParams.pageSize, + queryParams, + totalItemCount, + }); + // Hide page header when there is no actions index calling the setIsDataInResponse with false value. // Otherwise, it shows the page header calling the setIsDataInResponse with true value and it also keeps track // if the API request was done for the first time. @@ -248,310 +175,6 @@ export const ResponseActionsLog = memo< [setQueryParams] ); - // total actions - const totalItemCount = useMemo(() => actionList?.total ?? 0, [actionList]); - - // expanded tray contents - const toggleDetails = useCallback( - (item: ActionListApiResponse['data'][number]) => { - const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; - if (itemIdToExpandedRowMapValues[item.id]) { - delete itemIdToExpandedRowMapValues[item.id]; - } else { - const { - startedAt, - completedAt, - isCompleted, - wasSuccessful, - isExpired, - command: _command, - parameters, - } = item; - - const parametersList = parameters - ? Object.entries(parameters).map(([key, value]) => { - return `${key}:${value}`; - }) - : undefined; - - const command = getUiCommand(_command); - const dataList = [ - { - title: OUTPUT_MESSAGES.expandSection.placedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.startedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.completedAt, - description: `${completedAt ?? emptyValue}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.input, - description: `${command}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.parameters, - description: parametersList ? parametersList : emptyValue, - }, - ].map(({ title, description }) => { - return { - title: {title}, - description: {description}, - }; - }); - - const outputList = [ - { - title: ( - {`${OUTPUT_MESSAGES.expandSection.output}:`} - ), - description: ( - // codeblock for output - - {isExpired - ? OUTPUT_MESSAGES.hasExpired(command) - : isCompleted - ? wasSuccessful - ? OUTPUT_MESSAGES.wasSuccessful(command) - : OUTPUT_MESSAGES.hasFailed(command) - : OUTPUT_MESSAGES.isPending(command)} - - ), - }, - ]; - - itemIdToExpandedRowMapValues[item.id] = ( - <> - - - - - - - - - - ); - } - setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); - }, - [getTestId, itemIdToExpandedRowMap] - ); - // memoized callback for toggleDetails - const onClickCallback = useCallback( - (actionListDataItem: ActionListApiResponse['data'][number]) => () => - toggleDetails(actionListDataItem), - [toggleDetails] - ); - - // table column - const responseActionListColumns = useMemo(() => { - const columns = [ - { - field: 'startedAt', - name: TABLE_COLUMN_NAMES.time, - width: !showHostNames ? '21%' : '15%', - truncateText: true, - render: (startedAt: ActionListApiResponse['data'][number]['startedAt']) => { - return ( - - ); - }, - }, - { - field: 'command', - name: TABLE_COLUMN_NAMES.command, - width: !showHostNames ? '21%' : '10%', - truncateText: true, - render: (_command: ActionListApiResponse['data'][number]['command']) => { - const command = getUiCommand(_command); - return ( - - - {command} - - - ); - }, - }, - { - field: 'createdBy', - name: TABLE_COLUMN_NAMES.user, - width: !showHostNames ? '21%' : '14%', - truncateText: true, - render: (userId: ActionListApiResponse['data'][number]['createdBy']) => { - return ( - - } - > - - - {userId} - - - - ); - }, - }, - // conditional hostnames column - { - field: 'hosts', - name: TABLE_COLUMN_NAMES.hosts, - width: '20%', - truncateText: true, - render: (_hosts: ActionListApiResponse['data'][number]['hosts']) => { - const hosts = _hosts && Object.values(_hosts); - // join hostnames if the action is for multiple agents - // and skip empty strings for names if any - const _hostnames = hosts - .reduce((acc, host) => { - if (host.name.trim()) { - acc.push(host.name); - } - return acc; - }, []) - .join(', '); - - let hostnames = _hostnames; - if (!_hostnames) { - if (hosts.length > 1) { - // when action was for a single agent and no host name - hostnames = UX_MESSAGES.unenrolled.hosts; - } else if (hosts.length === 1) { - // when action was for a multiple agents - // and none of them have a host name - hostnames = UX_MESSAGES.unenrolled.host; - } - } - return ( - - - {hostnames} - - - ); - }, - }, - { - field: 'comment', - name: TABLE_COLUMN_NAMES.comments, - width: !showHostNames ? '21%' : '30%', - truncateText: true, - render: (comment: ActionListApiResponse['data'][number]['comment']) => { - return ( - - - {comment ?? emptyValue} - - - ); - }, - }, - { - field: 'status', - name: TABLE_COLUMN_NAMES.status, - width: !showHostNames ? '15%' : '10%', - render: (_status: ActionListApiResponse['data'][number]['status']) => { - const status = getActionStatus(_status); - - return ( - - - - ); - }, - }, - { - field: '', - align: RIGHT_ALIGNMENT as HorizontalAlignment, - width: '40px', - isExpander: true, - name: ( - - {UX_MESSAGES.screenReaderExpand} - - ), - render: (actionListDataItem: ActionListApiResponse['data'][number]) => { - return ( - - ); - }, - }, - ]; - // filter out the `hosts` column - // if showHostNames is FALSE - if (!showHostNames) { - return columns.filter((column) => column.field !== 'hosts'); - } - return columns; - }, [showHostNames, getTestId, itemIdToExpandedRowMap, onClickCallback]); - - // table pagination - const tablePagination = useMemo(() => { - const pageIndex = isFlyout ? (queryParams.page || 1) - 1 : paginationFromUrlParams.page - 1; - const pageSize = isFlyout ? queryParams.pageSize || 10 : paginationFromUrlParams.pageSize; - return { - // this controls the table UI page - // to match 0-based table paging - pageIndex, - pageSize, - totalItemCount, - pageSizeOptions: MANAGEMENT_PAGE_SIZE_OPTIONS as number[], - }; - }, [ - isFlyout, - paginationFromUrlParams.page, - paginationFromUrlParams.pageSize, - queryParams.page, - queryParams.pageSize, - totalItemCount, - ]); - // handle onChange const handleTableOnChange = useCallback( ({ page: _page }: CriteriaWithPagination) => { @@ -563,16 +186,12 @@ export const ResponseActionsLog = memo< page: index + 1, pageSize: size, }; - if (isFlyout) { - setQueryParams((prevState) => ({ - ...prevState, - ...pagingArgs, - })); - } else { - setQueryParams((prevState) => ({ - ...prevState, - ...pagingArgs, - })); + + setQueryParams((prevState) => ({ + ...prevState, + ...pagingArgs, + })); + if (!isFlyout) { setPaginationOnUrlParams({ ...pagingArgs, }); @@ -582,42 +201,6 @@ export const ResponseActionsLog = memo< [isFlyout, reFetchEndpointActionList, setQueryParams, setPaginationOnUrlParams] ); - // compute record ranges - const pagedResultsCount = useMemo(() => { - const page = queryParams.page ?? 1; - const perPage = queryParams?.pageSize ?? 10; - - const totalPages = Math.ceil(totalItemCount / perPage); - const fromCount = perPage * page - perPage + 1; - const toCount = - page === totalPages || totalPages === 1 ? totalItemCount : fromCount + perPage - 1; - return { fromCount, toCount }; - }, [queryParams.page, queryParams.pageSize, totalItemCount]); - - // create range label to display - const recordRangeLabel = useMemo( - () => ( - - - - {'-'} - - - ), - total: , - recordsLabel: {UX_MESSAGES.recordsLabel(totalItemCount)}, - }} - /> - - ), - [getTestId, pagedResultsCount.fromCount, pagedResultsCount.toCount, totalItemCount] - ); - if (error?.body?.statusCode === 404 && error?.body?.message === 'index_not_found_exception') { return ; } else if (isFetching && isFirstAttempt) { @@ -675,7 +258,7 @@ export const ResponseActionsLog = memo< columns={responseActionListColumns} itemId="id" itemIdToExpandedRowMap={itemIdToExpandedRowMap} - isExpandable={true} + isExpandable pagination={tablePagination} onChange={handleTableOnChange} loading={isFetching} diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx new file mode 100644 index 000000000000..e4dd30b46812 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx @@ -0,0 +1,441 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback, useMemo, useState } from 'react'; +import type { HorizontalAlignment } from '@elastic/eui'; + +import { + EuiI18nNumber, + EuiAvatar, + EuiButtonIcon, + EuiCodeBlock, + EuiDescriptionList, + EuiFacetButton, + EuiFlexGroup, + EuiFlexItem, + RIGHT_ALIGNMENT, + EuiScreenReaderOnly, + EuiText, + EuiToolTip, +} from '@elastic/eui'; +import { css, euiStyled } from '@kbn/kibana-react-plugin/common'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { ActionListApiResponse } from '../../../../common/endpoint/types'; +import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; +import { FormattedDate } from '../../../common/components/formatted_date'; +import { OUTPUT_MESSAGES, TABLE_COLUMN_NAMES, UX_MESSAGES } from './translations'; +import { getActionStatus, getUiCommand } from './components/hooks'; +import { getEmptyValue } from '../../../common/components/empty_value'; +import { StatusBadge } from './components/status_badge'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../common/constants'; + +const emptyValue = getEmptyValue(); + +// Truncated usernames +const StyledFacetButton = euiStyled(EuiFacetButton)` + .euiText { + margin-top: 0.38rem; + overflow-y: visible !important; + } +`; + +const customDescriptionListCss = css` + &.euiDescriptionList { + > .euiDescriptionList__title { + color: ${(props) => props.theme.eui.euiColorDarkShade}; + font-size: ${(props) => props.theme.eui.euiFontSizeXS}; + margin-top: ${(props) => props.theme.eui.euiSizeS}; + } + + > .euiDescriptionList__description { + font-weight: ${(props) => props.theme.eui.euiFontWeightSemiBold}; + margin-top: ${(props) => props.theme.eui.euiSizeS}; + } + } +`; + +const StyledDescriptionList = euiStyled(EuiDescriptionList).attrs({ + compressed: true, + type: 'column', +})` + ${customDescriptionListCss} +`; + +// output section styles +const topSpacingCss = css` + ${(props) => `${props.theme.eui.euiCodeBlockPaddingModifiers.paddingMedium} 0`} +`; +const dashedBorderCss = css` + ${(props) => `1px dashed ${props.theme.eui.euiColorDisabled}`}; +`; +const StyledDescriptionListOutput = euiStyled(EuiDescriptionList).attrs({ compressed: true })` + ${customDescriptionListCss} + dd { + margin: ${topSpacingCss}; + padding: ${topSpacingCss}; + border-top: ${dashedBorderCss}; + border-bottom: ${dashedBorderCss}; + } +`; + +// code block styles +const StyledEuiCodeBlock = euiStyled(EuiCodeBlock).attrs({ + transparentBackground: true, + paddingSize: 'none', +})` + code { + color: ${(props) => props.theme.eui.euiColorDarkShade} !important; + } +`; + +export const useResponseActionsLogTable = ({ + pageIndex, + pageSize, + queryParams, + showHostNames, + totalItemCount, +}: { + pageIndex: number; + pageSize: number; + queryParams: EndpointActionListRequestQuery; + showHostNames: boolean; + totalItemCount: number; +}) => { + const getTestId = useTestIdGenerator('response-actions-list'); + + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{ + [k: ActionListApiResponse['data'][number]['id']]: React.ReactNode; + }>({}); + + // expanded tray contents + const toggleDetails = useCallback( + (item: ActionListApiResponse['data'][number]) => { + const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; + if (itemIdToExpandedRowMapValues[item.id]) { + delete itemIdToExpandedRowMapValues[item.id]; + } else { + const { + startedAt, + completedAt, + isCompleted, + wasSuccessful, + isExpired, + command: _command, + parameters, + } = item; + + const parametersList = parameters + ? Object.entries(parameters).map(([key, value]) => { + return `${key}:${value}`; + }) + : undefined; + + const command = getUiCommand(_command); + const dataList = [ + { + title: OUTPUT_MESSAGES.expandSection.placedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.startedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.completedAt, + description: `${completedAt ?? emptyValue}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.input, + description: `${command}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.parameters, + description: parametersList ? parametersList : emptyValue, + }, + ].map(({ title, description }) => { + return { + title: {title}, + description: {description}, + }; + }); + + const outputList = [ + { + title: ( + {`${OUTPUT_MESSAGES.expandSection.output}:`} + ), + description: ( + // codeblock for output + + {isExpired + ? OUTPUT_MESSAGES.hasExpired(command) + : isCompleted + ? wasSuccessful + ? OUTPUT_MESSAGES.wasSuccessful(command) + : OUTPUT_MESSAGES.hasFailed(command) + : OUTPUT_MESSAGES.isPending(command)} + + ), + }, + ]; + + itemIdToExpandedRowMapValues[item.id] = ( + <> + + + + + + + + + + ); + } + setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); + }, + [getTestId, itemIdToExpandedRowMap] + ); + // memoized callback for toggleDetails + const onClickCallback = useCallback( + (actionListDataItem: ActionListApiResponse['data'][number]) => () => + toggleDetails(actionListDataItem), + [toggleDetails] + ); + + const responseActionListColumns = useMemo(() => { + const columns = [ + { + field: 'startedAt', + name: TABLE_COLUMN_NAMES.time, + width: !showHostNames ? '21%' : '15%', + truncateText: true, + render: (startedAt: ActionListApiResponse['data'][number]['startedAt']) => { + return ( + + ); + }, + }, + { + field: 'command', + name: TABLE_COLUMN_NAMES.command, + width: !showHostNames ? '21%' : '10%', + truncateText: true, + render: (_command: ActionListApiResponse['data'][number]['command']) => { + const command = getUiCommand(_command); + return ( + + + {command} + + + ); + }, + }, + { + field: 'createdBy', + name: TABLE_COLUMN_NAMES.user, + width: !showHostNames ? '21%' : '14%', + truncateText: true, + render: (userId: ActionListApiResponse['data'][number]['createdBy']) => { + return ( + + } + > + + + {userId} + + + + ); + }, + }, + // conditional hostnames column + { + field: 'hosts', + name: TABLE_COLUMN_NAMES.hosts, + width: '20%', + truncateText: true, + render: (_hosts: ActionListApiResponse['data'][number]['hosts']) => { + const hosts = _hosts && Object.values(_hosts); + // join hostnames if the action is for multiple agents + // and skip empty strings for names if any + const _hostnames = hosts + .reduce((acc, host) => { + if (host.name.trim()) { + acc.push(host.name); + } + return acc; + }, []) + .join(', '); + + let hostnames = _hostnames; + if (!_hostnames) { + if (hosts.length > 1) { + // when action was for a single agent and no host name + hostnames = UX_MESSAGES.unenrolled.hosts; + } else if (hosts.length === 1) { + // when action was for a multiple agents + // and none of them have a host name + hostnames = UX_MESSAGES.unenrolled.host; + } + } + return ( + + + {hostnames} + + + ); + }, + }, + { + field: 'comment', + name: TABLE_COLUMN_NAMES.comments, + width: !showHostNames ? '21%' : '30%', + truncateText: true, + render: (comment: ActionListApiResponse['data'][number]['comment']) => { + return ( + + + {comment ?? emptyValue} + + + ); + }, + }, + { + field: 'status', + name: TABLE_COLUMN_NAMES.status, + width: !showHostNames ? '15%' : '10%', + render: (_status: ActionListApiResponse['data'][number]['status']) => { + const status = getActionStatus(_status); + + return ( + + + + ); + }, + }, + { + field: '', + align: RIGHT_ALIGNMENT as HorizontalAlignment, + width: '40px', + isExpander: true, + name: ( + + {UX_MESSAGES.screenReaderExpand} + + ), + render: (actionListDataItem: ActionListApiResponse['data'][number]) => { + return ( + + ); + }, + }, + ]; + // filter out the `hosts` column + // if showHostNames is FALSE + if (!showHostNames) { + return columns.filter((column) => column.field !== 'hosts'); + } + return columns; + }, [showHostNames, getTestId, itemIdToExpandedRowMap, onClickCallback]); + + // table pagination + const tablePagination = useMemo(() => { + return { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: MANAGEMENT_PAGE_SIZE_OPTIONS as number[], + }; + }, [pageIndex, pageSize, totalItemCount]); + + // compute record ranges + const pagedResultsCount = useMemo(() => { + const page = queryParams.page ?? 1; + const perPage = queryParams?.pageSize ?? 10; + + const totalPages = Math.ceil(totalItemCount / perPage); + const fromCount = perPage * page - perPage + 1; + const toCount = + page === totalPages || totalPages === 1 ? totalItemCount : fromCount + perPage - 1; + return { fromCount, toCount }; + }, [queryParams.page, queryParams.pageSize, totalItemCount]); + + // create range label to display + const recordRangeLabel = useMemo( + () => ( + + + + {'-'} + + + ), + total: , + recordsLabel: {UX_MESSAGES.recordsLabel(totalItemCount)}, + }} + /> + + ), + [getTestId, pagedResultsCount.fromCount, pagedResultsCount.toCount, totalItemCount] + ); + + return { itemIdToExpandedRowMap, responseActionListColumns, recordRangeLabel, tablePagination }; +}; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts index 0804bef55b3e..cdb1041cda7e 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts @@ -11,6 +11,8 @@ import { useGetEndpointsList } from './use_get_endpoints_list'; import { HOST_METADATA_LIST_ROUTE } from '../../../../common/endpoint/constants'; import { useQuery as _useQuery } from '@tanstack/react-query'; import { endpointMetadataHttpMocks } from '../../pages/endpoint_hosts/mocks'; +import { EndpointStatus, HostStatus } from '../../../../common/endpoint/types'; +import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; const useQueryMock = _useQuery as jest.Mock; @@ -107,4 +109,127 @@ describe('useGetEndpointsList hook', () => { }) ); }); + + it('should also list inactive agents', async () => { + const getApiResponse = apiMocks.responseProvider.metadataList.getMockImplementation(); + + // set a few of the agents as inactive/unenrolled + apiMocks.responseProvider.metadataList.mockImplementation(() => { + if (getApiResponse) { + return { + ...getApiResponse(), + data: getApiResponse().data.map((item, i) => { + const isInactiveIndex = [0, 1, 3].includes(i); + return { + ...item, + host_status: isInactiveIndex ? HostStatus.INACTIVE : item.host_status, + metadata: { + ...item.metadata, + host: { + ...item.metadata.host, + hostname: isInactiveIndex + ? `${item.metadata.host.hostname}-inactive` + : item.metadata.host.hostname, + }, + Endpoint: { + ...item.metadata.Endpoint, + status: isInactiveIndex + ? EndpointStatus.unenrolled + : item.metadata.Endpoint.status, + }, + }, + }; + }), + }; + } + throw new Error('some error'); + }); + + // verify useGetEndpointsList hook returns the same inactive agents + const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: 'inactive' })); + expect( + res.data?.map((host) => host.name.split('-')[2]).filter((name) => name === 'inactive').length + ).toEqual(3); + }); + + it('should only list 50 agents when more than 50 in the metadata list API', async () => { + const getApiResponse = apiMocks.responseProvider.metadataList.getMockImplementation(); + + apiMocks.responseProvider.metadataList.mockImplementation(() => { + if (getApiResponse) { + const generator = new EndpointDocGenerator('seed'); + const total = 60; + const data = Array.from({ length: total }, () => { + const endpoint = { + metadata: generator.generateHostMetadata(), + host_status: HostStatus.UNHEALTHY, + }; + + generator.updateCommonInfo(); + + return endpoint; + }); + + return { + ...getApiResponse(), + data, + page: 0, + // this page size is not used by the hook (it uses the default of 50) + // this is only for the test + pageSize: 80, + total, + }; + } + throw new Error('some error'); + }); + + // verify useGetEndpointsList hook returns all 50 agents in the list + const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: '' })); + expect(res.data?.length).toEqual(50); + }); + + it('should only list 10 more agents when 50 or more agents are already selected', async () => { + const getApiResponse = apiMocks.responseProvider.metadataList.getMockImplementation(); + + apiMocks.responseProvider.metadataList.mockImplementation(() => { + if (getApiResponse) { + const generator = new EndpointDocGenerator('seed'); + const total = 61; + const data = Array.from({ length: total }, () => { + const endpoint = { + metadata: generator.generateHostMetadata(), + host_status: HostStatus.UNHEALTHY, + }; + + generator.updateCommonInfo(); + + return endpoint; + }); + + return { + ...getApiResponse(), + data, + page: 0, + // since we're mocking that all 50 agents are selected + // page size is set to max allowed + pageSize: 10000, + total, + }; + } + throw new Error('some error'); + }); + + // get the first 50 agents to select + const agentIdsToSelect = apiMocks.responseProvider + .metadataList() + .data.map((d) => d.metadata.agent.id) + .slice(0, 50); + + // call useGetEndpointsList with all 50 agents selected + const res = await renderReactQueryHook(() => + useGetEndpointsList({ searchString: '', selectedAgentIds: agentIdsToSelect }) + ); + // verify useGetEndpointsList hook returns 60 agents + expect(res.data?.length).toEqual(60); + }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts new file mode 100644 index 000000000000..51326c8adbd1 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import type { KibanaResponseFactory, RequestHandler, RouteConfig } from '@kbn/core/server'; +import { + coreMock, + elasticsearchServiceMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, + savedObjectsClientMock, +} from '@kbn/core/server/mocks'; +import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; +import { ENDPOINTS_ACTION_LIST_ROUTE } from '../../../../common/endpoint/constants'; +import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; +import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; +import { EndpointAppContextService } from '../../endpoint_app_context_services'; +import { + createMockEndpointAppContextServiceSetupContract, + createMockEndpointAppContextServiceStartContract, + createRouteHandlerContext, +} from '../../mocks'; +import { registerActionListRoutes } from './list'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; +import { doesLogsEndpointActionsIndexExist } from '../../utils'; +import { getActionList, getActionListByStatus } from '../../services'; + +jest.mock('../../utils'); +const mockDoesLogsEndpointActionsIndexExist = doesLogsEndpointActionsIndexExist as jest.Mock; + +jest.mock('../../services'); +const mockGetActionList = getActionList as jest.Mock; +const mockGetActionListByStatus = getActionListByStatus as jest.Mock; + +describe(' Action List Handler', () => { + let endpointAppContextService: EndpointAppContextService; + let mockResponse: jest.Mocked; + + let actionListHandler: ( + query?: EndpointActionListRequestQuery + ) => Promise>; + + beforeEach(() => { + const esClientMock = elasticsearchServiceMock.createScopedClusterClient(); + const routerMock = httpServiceMock.createRouter(); + endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); + endpointAppContextService.start(createMockEndpointAppContextServiceStartContract()); + mockDoesLogsEndpointActionsIndexExist.mockResolvedValue(true); + + registerActionListRoutes(routerMock, { + logFactory: loggingSystemMock.create(), + service: endpointAppContextService, + config: () => Promise.resolve(createMockConfig()), + experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental), + }); + + actionListHandler = async ( + query?: EndpointActionListRequestQuery + ): Promise> => { + const req = httpServerMock.createKibanaRequest({ + query, + }); + mockResponse = httpServerMock.createResponseFactory(); + const [, routeHandler]: [ + RouteConfig, + RequestHandler< + unknown, + EndpointActionListRequestQuery, + unknown, + SecuritySolutionRequestHandlerContext + > + ] = routerMock.get.mock.calls.find(([{ path }]) => + path.startsWith(ENDPOINTS_ACTION_LIST_ROUTE) + )!; + await routeHandler( + coreMock.createCustomRequestHandlerContext( + createRouteHandlerContext(esClientMock, savedObjectsClientMock.create()) + ) as SecuritySolutionRequestHandlerContext, + req, + mockResponse + ); + + return mockResponse; + }; + }); + + afterEach(() => { + endpointAppContextService.stop(); + }); + + describe('Internals', () => { + it('should return `notFound` when actions index does not exist', async () => { + mockDoesLogsEndpointActionsIndexExist.mockResolvedValue(false); + await actionListHandler({ pageSize: 10, page: 1 }); + expect(mockResponse.notFound).toHaveBeenCalledWith({ + body: 'index_not_found_exception', + }); + }); + + it('should return `ok` when actions index exists', async () => { + await actionListHandler({ pageSize: 10, page: 1 }); + expect(mockResponse.ok).toHaveBeenCalled(); + }); + + it('should call `getActionListByStatus` when statuses filter values are provided', async () => { + await actionListHandler({ pageSize: 10, page: 1, statuses: ['failed', 'pending'] }); + expect(mockGetActionListByStatus).toBeCalledWith( + expect.objectContaining({ statuses: ['failed', 'pending'] }) + ); + }); + + it('should correctly format the request when calling `getActionListByStatus`', async () => { + await actionListHandler({ + agentIds: 'agentX', + commands: 'running-processes', + statuses: 'failed', + userIds: 'userX', + }); + expect(mockGetActionListByStatus).toBeCalledWith( + expect.objectContaining({ + elasticAgentIds: ['agentX'], + commands: ['running-processes'], + statuses: ['failed'], + userIds: ['userX'], + }) + ); + }); + + it('should call `getActionList` when statuses filter values are not provided', async () => { + await actionListHandler({ + pageSize: 10, + page: 1, + commands: ['isolate', 'kill-process'], + userIds: ['userX', 'userY'], + }); + expect(mockGetActionList).toBeCalledWith( + expect.objectContaining({ + commands: ['isolate', 'kill-process'], + userIds: ['userX', 'userY'], + }) + ); + }); + + it('should correctly format the request when calling `getActionList`', async () => { + await actionListHandler({ + page: 1, + pageSize: 10, + agentIds: 'agentX', + commands: 'isolate', + userIds: 'userX', + }); + + expect(mockGetActionList).toHaveBeenCalledWith( + expect.objectContaining({ + commands: ['isolate'], + elasticAgentIds: ['agentX'], + userIds: ['userX'], + }) + ); + }); + }); +}); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.fixtures.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.fixtures.ts index 281ba95d8e0c..3a81ad1c71dc 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.fixtures.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.fixtures.ts @@ -247,8 +247,8 @@ export function generateMetadataDocs(timestamp: number) { dataset: 'endpoint.metadata', }, host: { - hostname: 'rezzani-7.example.com', - name: 'rezzani-7.example.com', + hostname: 'Example-host-name-XYZ', + name: 'Example-host-name-XYZ', id: 'fc0ff548-feba-41b6-8367-65e8790d0eaf', ip: ['10.101.149.26', '2606:a000:ffc0:39:11ef:37b9:3371:578c'], mac: ['e2-6d-f9-0-46-2e'], @@ -399,8 +399,8 @@ export function generateMetadataDocs(timestamp: number) { }, host: { architecture: 'x86', - hostname: 'rezzani-7.example.com', - name: 'rezzani-7.example.com', + hostname: 'Example-host-name-XYZ', + name: 'Example-host-name-XYZ', id: 'fc0ff548-feba-41b6-8367-65e8790d0eaf', ip: ['10.101.149.26', '2606:a000:ffc0:39:11ef:37b9:3371:578c'], mac: ['e2-6d-f9-0-46-2e'], @@ -550,8 +550,8 @@ export function generateMetadataDocs(timestamp: number) { }, host: { architecture: 'x86', - hostname: 'rezzani-7.example.com', - name: 'rezzani-7.example.com', + hostname: 'Example-host-name-XYZ', + name: 'Example-host-name-XYZ', id: 'fc0ff548-feba-41b6-8367-65e8790d0eaf', ip: ['10.101.149.26', '2606:a000:ffc0:39:11ef:37b9:3371:578c'], mac: ['e2-6d-f9-0-46-2e'], diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index 508aa8884fcf..0ee6b4495257 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -259,6 +259,26 @@ export default function ({ getService }: FtrProviderContext) { expect(body.pageSize).to.eql(10); }); + it('metadata api should return the endpoint based on the agent hostname', async () => { + const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf'; + const targetAgentHostname = 'Example-host-name-XYZ'; + const { body } = await supertest + .get(HOST_METADATA_LIST_ROUTE) + .set('kbn-xsrf', 'xxx') + .query({ + kuery: `united.endpoint.host.hostname:${targetAgentHostname}`, + }) + .expect(200); + expect(body.total).to.eql(1); + const resultHostId: string = body.data[0].metadata.host.id; + const resultElasticAgentName: string = body.data[0].metadata.host.hostname; + expect(resultHostId).to.eql(targetEndpointId); + expect(resultElasticAgentName).to.eql(targetAgentHostname); + expect(body.data.length).to.eql(1); + expect(body.page).to.eql(0); + expect(body.pageSize).to.eql(10); + }); + it('metadata api should return all hosts when filter is empty string', async () => { const { body } = await supertest .get(HOST_METADATA_LIST_ROUTE) From 54ad60464d3642ddd6659253bc67c2e189d012d3 Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Tue, 27 Sep 2022 10:27:32 -0700 Subject: [PATCH 031/185] [automation] Update references to bundled packages (#141818) Co-authored-by: apmmachine --- fleet_packages.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fleet_packages.json b/fleet_packages.json index c4a7f87127f1..4651e8628758 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -20,7 +20,7 @@ [ { "name": "apm", - "version": "8.4.0", + "version": "8.6.0-preview-1663775281", "forceAlignStackVersion": true }, { @@ -29,7 +29,7 @@ }, { "name": "endpoint", - "version": "8.4.1" + "version": "8.5.0" }, { "name": "fleet_server", @@ -39,4 +39,4 @@ "name": "synthetics", "version": "0.10.2" } -] +] \ No newline at end of file From 02f874bb01567a0c397fcc91b74c3ae47a240054 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Tue, 27 Sep 2022 13:36:34 -0400 Subject: [PATCH 032/185] Hide curl request in Search Index pipelines tab for connector and crawler indices (#141976) --- .../pipelines/ingest_pipelines_card.tsx | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines_card.tsx index ca6140b7b962..7bf1ef06e1e7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines_card.tsx @@ -26,6 +26,7 @@ import { KibanaLogic } from '../../../../shared/kibana'; import { LicensingLogic } from '../../../../shared/licensing'; import { CreateCustomPipelineApiLogic } from '../../../api/index/create_custom_pipeline_api_logic'; import { FetchCustomPipelineApiLogic } from '../../../api/index/fetch_custom_pipeline_api_logic'; +import { isApiIndex } from '../../../utils/indices'; import { CurlRequest } from '../components/curl_request/curl_request'; import { IndexViewLogic } from '../index_view_logic'; @@ -36,7 +37,7 @@ import { PipelinesLogic } from './pipelines_logic'; export const IngestPipelinesCard: React.FC = () => { const { indexName } = useValues(IndexViewLogic); - const { canSetPipeline, pipelineState, showModal } = useValues(PipelinesLogic); + const { canSetPipeline, index, pipelineState, showModal } = useValues(PipelinesLogic); const { closeModal, openModal, setPipelineState, savePipeline } = useActions(PipelinesLogic); const { makeRequest: fetchCustomPipeline } = useActions(FetchCustomPipelineApiLogic); const { makeRequest: createCustomPipeline } = useActions(CreateCustomPipelineApiLogic); @@ -97,22 +98,24 @@ export const IngestPipelinesCard: React.FC = () => {
    - - - - - - + + {isApiIndex(index) && ( + + + + + + )} {i18n.translate( From fb55d8d6c151ac9f7070a7d1ccf6fdbe01020cbe Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 27 Sep 2022 19:51:37 +0200 Subject: [PATCH 033/185] [ML] Fix expanded row layout in the Nodes table (#141964) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../nodes_overview/allocated_models.tsx | 1 + .../nodes_overview/expanded_row.tsx | 48 ++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx index 0da7778c6e2b..d1808c2c8213 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx @@ -190,6 +190,7 @@ export const AllocatedModels: FC = ({ })} onTableChange={() => {}} data-test-subj={'mlNodesAllocatedModels'} + css={{ overflow: 'auto' }} /> ); }; diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx index 4c9690486278..a6b6adc4fbc2 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx @@ -17,6 +17,7 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { cloneDeep } from 'lodash'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import { css } from '@emotion/react'; import { NodeItem } from './nodes_list'; import { useListItemsFormatter } from '../models_management/expanded_row'; import { AllocatedModels } from './allocated_models'; @@ -44,10 +45,12 @@ export const ExpandedRow: FC = ({ item }) => { attributes['ml.max_jvm_size'] = bytesFormatter(attributes['ml.max_jvm_size']); return ( - <> - - - +
    + @@ -85,25 +88,26 @@ export const ExpandedRow: FC = ({ item }) => { /> + - {allocatedModels.length > 0 ? ( - - - -
    - -
    -
    - + {allocatedModels.length > 0 ? ( + <> + + + +
    + +
    +
    + - -
    -
    - ) : null} - - + + + + ) : null} +
    ); }; From 2aebaf720ea805fdd2b5545251f4944c56c873c8 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Tue, 27 Sep 2022 13:57:54 -0400 Subject: [PATCH 034/185] Updating no matches text (#141981) --- .../user_profiles/no_matches.test.tsx | 2 +- .../components/user_profiles/no_matches.tsx | 21 ++++++++++++++++--- .../components/user_profiles/translations.ts | 15 +++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/cases/public/components/user_profiles/no_matches.test.tsx b/x-pack/plugins/cases/public/components/user_profiles/no_matches.test.tsx index 3471aad3fec3..e2d23dfd988e 100644 --- a/x-pack/plugins/cases/public/components/user_profiles/no_matches.test.tsx +++ b/x-pack/plugins/cases/public/components/user_profiles/no_matches.test.tsx @@ -13,6 +13,6 @@ describe('NoMatches', () => { it('renders the no matches messages', () => { render(); - expect(screen.getByText('No matching users with required access.')); + expect(screen.getByText("User doesn't exist or is unavailable")); }); }); diff --git a/x-pack/plugins/cases/public/components/user_profiles/no_matches.tsx b/x-pack/plugins/cases/public/components/user_profiles/no_matches.tsx index 638d705fade8..ccd27b123f23 100644 --- a/x-pack/plugins/cases/public/components/user_profiles/no_matches.tsx +++ b/x-pack/plugins/cases/public/components/user_profiles/no_matches.tsx @@ -5,7 +5,15 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer, EuiText, EuiTextAlign } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiSpacer, + EuiText, + EuiTextAlign, +} from '@elastic/eui'; import React from 'react'; import * as i18n from './translations'; @@ -25,11 +33,18 @@ const NoMatchesComponent: React.FC = () => { - {i18n.NO_MATCHING_USERS} + {i18n.USER_DOES_NOT_EXIST}
    - {i18n.TRY_MODIFYING_SEARCH} + {i18n.MODIFY_SEARCH} +
    + + {i18n.LEARN_PRIVILEGES_GRANT_ACCESS} +
    diff --git a/x-pack/plugins/cases/public/components/user_profiles/translations.ts b/x-pack/plugins/cases/public/components/user_profiles/translations.ts index db76408759be..2624ec834cd2 100644 --- a/x-pack/plugins/cases/public/components/user_profiles/translations.ts +++ b/x-pack/plugins/cases/public/components/user_profiles/translations.ts @@ -44,12 +44,19 @@ export const ASSIGNEES = i18n.translate('xpack.cases.userProfile.assigneesTitle' defaultMessage: 'Assignees', }); -export const NO_MATCHING_USERS = i18n.translate('xpack.cases.userProfiles.noMatchingUsers', { - defaultMessage: 'No matching users with required access.', +export const USER_DOES_NOT_EXIST = i18n.translate('xpack.cases.userProfiles.userDoesNotExist', { + defaultMessage: "User doesn't exist or is unavailable", }); -export const TRY_MODIFYING_SEARCH = i18n.translate('xpack.cases.userProfiles.tryModifyingSearch', { - defaultMessage: 'Try modifying your search.', +export const LEARN_PRIVILEGES_GRANT_ACCESS = i18n.translate( + 'xpack.cases.userProfiles.learnPrivileges', + { + defaultMessage: 'Learn what privileges grant access to cases.', + } +); + +export const MODIFY_SEARCH = i18n.translate('xpack.cases.userProfiles.modifySearch', { + defaultMessage: "Modify your search or check the user's privileges.", }); export const INVALID_ASSIGNEES = i18n.translate('xpack.cases.create.invalidAssignees', { From aeb7a65b5fa30e056cf116952a083e132fc906ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 27 Sep 2022 14:06:07 -0400 Subject: [PATCH 035/185] [APM] rename tech preview feedback copy (#141857) --- .../observability/server/ui_settings.ts | 49 +++++-------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index 69e7f0ba51af..a5b111569e31 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -29,11 +29,16 @@ import { const technicalPreviewLabel = i18n.translate( 'xpack.observability.uiSettings.technicalPreviewLabel', - { - defaultMessage: 'technical preview', - } + { defaultMessage: 'technical preview' } ); +function feedbackLink({ href }: { href: string }) { + return `${i18n.translate( + 'xpack.observability.uiSettings.giveFeedBackLabel', + { defaultMessage: 'Give feedback' } + )}`; +} + type UiSettings = UiSettingsParams & { showInLabs?: boolean }; /** @@ -167,12 +172,7 @@ export const uiSettings: Record = { '{technicalPreviewLabel} Enable the Service groups feature on APM UI. {feedbackLink}.', values: { technicalPreviewLabel: `[${technicalPreviewLabel}]`, - feedbackLink: - '' + - i18n.translate('xpack.observability.enableServiceGroups.feedbackLinkText', { - defaultMessage: 'Give feedback', - }) + - '', + feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-service-groups' }), }, }), schema: schema.boolean(), @@ -206,15 +206,7 @@ export const uiSettings: Record = { '{technicalPreviewLabel} Default APM Service Inventory page sort (for Services without Machine Learning applied) to sort by Service Name. {feedbackLink}.', values: { technicalPreviewLabel: `[${technicalPreviewLabel}]`, - feedbackLink: - '' + - i18n.translate( - 'xpack.observability.apmServiceInventoryOptimizedSorting.feedbackLinkText', - { - defaultMessage: 'Give feedback', - } - ) + - '', + feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-apm-page-performance' }), }, } ), @@ -245,12 +237,7 @@ export const uiSettings: Record = { '{technicalPreviewLabel} Enable the APM Trace Explorer feature, that allows you to search and inspect traces with KQL or EQL. {feedbackLink}.', values: { technicalPreviewLabel: `[${technicalPreviewLabel}]`, - feedbackLink: - '' + - i18n.translate('xpack.observability.apmTraceExplorerTabDescription.feedbackLinkText', { - defaultMessage: 'Give feedback', - }) + - '', + feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-trace-explorer' }), }, }), schema: schema.boolean(), @@ -269,12 +256,7 @@ export const uiSettings: Record = { '{technicalPreviewLabel} Enable the APM Operations Breakdown feature, that displays aggregates for backend operations. {feedbackLink}.', values: { technicalPreviewLabel: `[${technicalPreviewLabel}]`, - feedbackLink: - '' + - i18n.translate('xpack.observability.apmOperationsBreakdownDescription.feedbackLinkText', { - defaultMessage: 'Give feedback', - }) + - '', + feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-operations-breakdown' }), }, }), schema: schema.boolean(), @@ -318,12 +300,7 @@ export const uiSettings: Record = { '{technicalPreviewLabel} Display Amazon Lambda metrics in the service metrics tab. {feedbackLink}', values: { technicalPreviewLabel: `[${technicalPreviewLabel}]`, - feedbackLink: - '' + - i18n.translate('xpack.observability.awsLambdaDescription', { - defaultMessage: 'Send feedback', - }) + - '', + feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-aws-lambda' }), }, }), schema: schema.boolean(), From bad534f71b7f8b975b392794195dcf36eee60759 Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:44:37 -0600 Subject: [PATCH 036/185] [RAM] Split bulk snooze options into 4 menu options (snooze/unsnooze/schedule/unschedule) (#141785) * Split bulk snooze options into 4 items * Update x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_modal.tsx Co-authored-by: Lisa Cawley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Lisa Cawley --- .../rule_quick_edit_buttons.test.tsx | 55 ++++++++++-- .../components/rule_quick_edit_buttons.tsx | 84 ++++++++++++++++++ .../components/bulk_snooze_modal.tsx | 86 +++++++++++++++---- .../components/bulk_snooze_schedule_modal.tsx | 49 ++++++----- .../rules_list/components/rules_list.test.tsx | 67 ++++++++++++++- .../rules_list/components/rules_list.tsx | 43 +++++++++- 6 files changed, 337 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx index 4e6ae5c7cdb9..1605f09ebc0f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx @@ -20,9 +20,13 @@ jest.mock('../../../../common/lib/kibana', () => ({ const setRulesToUpdateAPIKey = jest.fn(); const setRulesToSnooze = jest.fn(); +const setRulesToUnsnooze = jest.fn(); const setRulesToSchedule = jest.fn(); +const setRulesToUnschedule = jest.fn(); const setRulesToSnoozeFilter = jest.fn(); +const setRulesToUnsnoozeFilter = jest.fn(); const setRulesToScheduleFilter = jest.fn(); +const setRulesToUnscheduleFilter = jest.fn(); const setRulesToUpdateAPIKeyFilter = jest.fn(); describe('rule_quick_edit_buttons', () => { @@ -46,9 +50,13 @@ describe('rule_quick_edit_buttons', () => { setRulesToDelete={() => {}} setRulesToUpdateAPIKey={() => {}} setRulesToSnooze={() => {}} + setRulesToUnsnooze={() => {}} setRulesToSchedule={() => {}} + setRulesToUnschedule={() => {}} setRulesToSnoozeFilter={() => {}} + setRulesToUnsnoozeFilter={() => {}} setRulesToScheduleFilter={() => {}} + setRulesToUnscheduleFilter={() => {}} setRulesToUpdateAPIKeyFilter={() => {}} /> ); @@ -58,7 +66,9 @@ describe('rule_quick_edit_buttons', () => { expect(wrapper.find('[data-test-subj="updateAPIKeys"]').exists()).toBeTruthy(); expect(wrapper.find('[data-test-subj="deleteAll"]').exists()).toBeTruthy(); expect(wrapper.find('[data-test-subj="bulkSnooze"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="bulkUnsnooze"]').exists()).toBeTruthy(); expect(wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="bulkRemoveSnoozeSchedule"]').exists()).toBeTruthy(); }); it('renders enableAll if rules are all disabled', async () => { @@ -77,9 +87,13 @@ describe('rule_quick_edit_buttons', () => { setRulesToDelete={() => {}} setRulesToUpdateAPIKey={() => {}} setRulesToSnooze={() => {}} + setRulesToUnsnooze={() => {}} setRulesToSchedule={() => {}} + setRulesToUnschedule={() => {}} setRulesToSnoozeFilter={() => {}} + setRulesToUnsnoozeFilter={() => {}} setRulesToScheduleFilter={() => {}} + setRulesToUnscheduleFilter={() => {}} setRulesToUpdateAPIKeyFilter={() => {}} /> ); @@ -104,20 +118,27 @@ describe('rule_quick_edit_buttons', () => { setRulesToDelete={() => {}} setRulesToUpdateAPIKey={() => {}} setRulesToSnooze={() => {}} + setRulesToUnsnooze={() => {}} setRulesToSchedule={() => {}} + setRulesToUnschedule={() => {}} setRulesToSnoozeFilter={() => {}} + setRulesToUnsnoozeFilter={() => {}} setRulesToScheduleFilter={() => {}} + setRulesToUnscheduleFilter={() => {}} setRulesToUpdateAPIKeyFilter={() => {}} /> ); expect(wrapper.find('[data-test-subj="disableAll"]').first().prop('isDisabled')).toBeTruthy(); expect(wrapper.find('[data-test-subj="deleteAll"]').first().prop('isDisabled')).toBeTruthy(); - - expect(wrapper.find('[data-test-subj="updateAPIKeys"]').first().prop('isDiabled')).toBeFalsy(); - expect(wrapper.find('[data-test-subj="bulkSnooze"]').first().prop('isDiabled')).toBeFalsy(); + expect(wrapper.find('[data-test-subj="updateAPIKeys"]').first().prop('isDisabled')).toBeFalsy(); + expect(wrapper.find('[data-test-subj="bulkSnooze"]').first().prop('isDisabled')).toBeFalsy(); + expect(wrapper.find('[data-test-subj="bulkUnsnooze"]').first().prop('isDisabled')).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').first().prop('isDisabled') + ).toBeFalsy(); expect( - wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').first().prop('isDiabled') + wrapper.find('[data-test-subj="bulkRemoveSnoozeSchedule"]').first().prop('isDisabled') ).toBeFalsy(); }); @@ -137,10 +158,14 @@ describe('rule_quick_edit_buttons', () => { onActionPerformed={() => {}} setRulesToDelete={() => {}} setRulesToSnooze={setRulesToSnooze} + setRulesToUnsnooze={setRulesToUnsnooze} setRulesToSchedule={setRulesToSchedule} + setRulesToUnschedule={setRulesToUnschedule} setRulesToUpdateAPIKey={setRulesToUpdateAPIKey} setRulesToSnoozeFilter={setRulesToSnoozeFilter} + setRulesToUnsnoozeFilter={setRulesToUnsnoozeFilter} setRulesToScheduleFilter={setRulesToScheduleFilter} + setRulesToUnscheduleFilter={setRulesToUnscheduleFilter} setRulesToUpdateAPIKeyFilter={setRulesToUpdateAPIKeyFilter} /> ); @@ -148,14 +173,22 @@ describe('rule_quick_edit_buttons', () => { wrapper.find('[data-test-subj="bulkSnooze"]').first().simulate('click'); expect(setRulesToSnooze).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkUnsnooze"]').first().simulate('click'); + expect(setRulesToUnsnooze).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').first().simulate('click'); expect(setRulesToSchedule).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkRemoveSnoozeSchedule"]').first().simulate('click'); + expect(setRulesToUnschedule).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="updateAPIKeys"]').first().simulate('click'); expect(setRulesToUpdateAPIKey).toHaveBeenCalledTimes(1); expect(setRulesToSnoozeFilter).not.toHaveBeenCalled(); + expect(setRulesToUnsnoozeFilter).not.toHaveBeenCalled(); expect(setRulesToScheduleFilter).not.toHaveBeenCalled(); + expect(setRulesToUnscheduleFilter).not.toHaveBeenCalled(); expect(setRulesToUpdateAPIKeyFilter).not.toHaveBeenCalled(); }); @@ -175,10 +208,14 @@ describe('rule_quick_edit_buttons', () => { onActionPerformed={() => {}} setRulesToDelete={() => {}} setRulesToSnooze={setRulesToSnooze} + setRulesToUnsnooze={setRulesToUnsnooze} setRulesToSchedule={setRulesToSchedule} + setRulesToUnschedule={setRulesToUnschedule} setRulesToUpdateAPIKey={setRulesToUpdateAPIKey} setRulesToSnoozeFilter={setRulesToSnoozeFilter} + setRulesToUnsnoozeFilter={setRulesToUnsnoozeFilter} setRulesToScheduleFilter={setRulesToScheduleFilter} + setRulesToUnscheduleFilter={setRulesToUnscheduleFilter} setRulesToUpdateAPIKeyFilter={setRulesToUpdateAPIKeyFilter} /> ); @@ -186,14 +223,22 @@ describe('rule_quick_edit_buttons', () => { wrapper.find('[data-test-subj="bulkSnooze"]').first().simulate('click'); expect(setRulesToSnoozeFilter).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkUnsnooze"]').first().simulate('click'); + expect(setRulesToUnsnoozeFilter).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').first().simulate('click'); expect(setRulesToScheduleFilter).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="bulkRemoveSnoozeSchedule"]').first().simulate('click'); + expect(setRulesToUnscheduleFilter).toHaveBeenCalledTimes(1); + wrapper.find('[data-test-subj="updateAPIKeys"]').first().simulate('click'); expect(setRulesToUpdateAPIKeyFilter).toHaveBeenCalledTimes(1); - expect(setRulesToSchedule).not.toHaveBeenCalled(); expect(setRulesToSnooze).not.toHaveBeenCalled(); + expect(setRulesToUnsnooze).not.toHaveBeenCalled(); + expect(setRulesToSchedule).not.toHaveBeenCalled(); + expect(setRulesToUnschedule).not.toHaveBeenCalled(); expect(setRulesToUpdateAPIKey).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx index 0b7db1ebeceb..c91c461993a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx @@ -25,14 +25,20 @@ export type ComponentOpts = { onPerformingAction?: () => void; onActionPerformed?: () => void; isSnoozingRules?: boolean; + isUnsnoozingRules?: boolean; isSchedulingRules?: boolean; + isUnschedulingRules?: boolean; isUpdatingRuleAPIKeys?: boolean; setRulesToDelete: React.Dispatch>; setRulesToUpdateAPIKey: React.Dispatch>; setRulesToSnooze: React.Dispatch>; + setRulesToUnsnooze: React.Dispatch>; setRulesToSchedule: React.Dispatch>; + setRulesToUnschedule: React.Dispatch>; setRulesToSnoozeFilter: React.Dispatch>; + setRulesToUnsnoozeFilter: React.Dispatch>; setRulesToScheduleFilter: React.Dispatch>; + setRulesToUnscheduleFilter: React.Dispatch>; setRulesToUpdateAPIKeyFilter: React.Dispatch>; } & BulkOperationsComponentOpts; @@ -65,16 +71,22 @@ export const RuleQuickEditButtons: React.FunctionComponent = ({ onPerformingAction = noop, onActionPerformed = noop, isSnoozingRules = false, + isUnsnoozingRules = false, isSchedulingRules = false, + isUnschedulingRules = false, isUpdatingRuleAPIKeys = false, enableRules, disableRules, setRulesToDelete, setRulesToUpdateAPIKey, setRulesToSnooze, + setRulesToUnsnooze, setRulesToSchedule, + setRulesToUnschedule, setRulesToSnoozeFilter, + setRulesToUnsnoozeFilter, setRulesToScheduleFilter, + setRulesToUnscheduleFilter, setRulesToUpdateAPIKeyFilter, }: ComponentOpts) => { const { @@ -90,7 +102,9 @@ export const RuleQuickEditButtons: React.FunctionComponent = ({ isDisablingRules || isDeletingRules || isSnoozingRules || + isUnsnoozingRules || isSchedulingRules || + isUnschedulingRules || isUpdatingRuleAPIKeys; const allRulesDisabled = useMemo(() => { @@ -220,6 +234,28 @@ export const RuleQuickEditButtons: React.FunctionComponent = ({ } } + async function onUnsnoozeAllClick() { + onPerformingAction(); + try { + if (isAllSelected) { + setRulesToUnsnoozeFilter(getFilter()); + } else { + setRulesToUnsnooze(selectedItems); + } + } catch (e) { + toasts.addDanger({ + title: i18n.translate( + 'xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToSnoozeRules', + { + defaultMessage: 'Failed to snooze or unsnooze rules', + } + ), + }); + } finally { + onActionPerformed(); + } + } + async function onScheduleAllClick() { onPerformingAction(); try { @@ -242,6 +278,28 @@ export const RuleQuickEditButtons: React.FunctionComponent = ({ } } + async function onUnscheduleAllClick() { + onPerformingAction(); + try { + if (isAllSelected) { + setRulesToUnscheduleFilter(getFilter()); + } else { + setRulesToUnschedule(selectedItems); + } + } catch (e) { + toasts.addDanger({ + title: i18n.translate( + 'xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToSnoozeRules', + { + defaultMessage: 'Failed to snooze or unsnooze rules', + } + ), + }); + } finally { + onActionPerformed(); + } + } + return ( = ({ />
    + + + + + = ({ /> + + + + + void; onSave: () => void; - setIsLoading: (isLoading: boolean) => void; + setIsSnoozingRule: (isLoading: boolean) => void; + setIsUnsnoozingRule: (isLoading: boolean) => void; onSearchPopulate?: (filter: string) => void; } & BulkOperationsComponentOpts; @@ -43,13 +47,29 @@ const failureMessage = i18n.translate( } ); +const deleteConfirmPlural = (total: number) => + i18n.translate('xpack.triggersActionsUI.sections.rulesList.bulkUnsnoozeConfirmationPlural', { + defaultMessage: 'Unsnooze {total, plural, one {# rule} other {# rules}}? ', + values: { total }, + }); + +const deleteConfirmSingle = (ruleName: string) => + i18n.translate('xpack.triggersActionsUI.sections.rulesList.bulkUnsnoozeConfirmationSingle', { + defaultMessage: 'Unsnooze {ruleName}?', + values: { ruleName }, + }); + export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { const { rulesToSnooze, + rulesToUnsnooze, rulesToSnoozeFilter, + rulesToUnsnoozeFilter, + numberOfSelectedRules = 0, onClose, onSave, - setIsLoading, + setIsSnoozingRule, + setIsUnsnoozingRule, onSearchPopulate, bulkSnoozeRules, bulkUnsnoozeRules, @@ -68,12 +88,12 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { return rulesToSnooze.length > 0; }, [rulesToSnooze, rulesToSnoozeFilter]); - const isSnoozed = useMemo(() => { - if (rulesToSnoozeFilter) { + const isUnsnoozeModalOpen = useMemo(() => { + if (rulesToUnsnoozeFilter) { return true; } - return rulesToSnooze.some((item) => isRuleSnoozed(item)); - }, [rulesToSnooze, rulesToSnoozeFilter]); + return rulesToUnsnooze.length > 0; + }, [rulesToUnsnooze, rulesToUnsnoozeFilter]); const interval = useMemo(() => { if (rulesToSnoozeFilter) { @@ -87,7 +107,7 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { const onSnoozeRule = async (schedule: SnoozeSchedule) => { onClose(); - setIsLoading(true); + setIsSnoozingRule(true); try { const response = await bulkSnoozeRules({ ids: rulesToSnooze.map((item) => item.id), @@ -100,18 +120,17 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { title: failureMessage, }); } - setIsLoading(false); + setIsSnoozingRule(false); onSave(); }; - const onUnsnoozeRule = async (scheduleIds?: string[]) => { + const onUnsnoozeRule = async () => { onClose(); - setIsLoading(true); + setIsUnsnoozingRule(true); try { const response = await bulkUnsnoozeRules({ - ids: rulesToSnooze.map((item) => item.id), - filter: rulesToSnoozeFilter, - scheduleIds, + ids: rulesToUnsnooze.map((item) => item.id), + filter: rulesToUnsnoozeFilter, }); showToast(response, 'snooze'); } catch (error) { @@ -119,10 +138,42 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { title: failureMessage, }); } - setIsLoading(false); + setIsUnsnoozingRule(false); onSave(); }; + const confirmationTitle = useMemo(() => { + if (!rulesToUnsnoozeFilter && numberOfSelectedRules === 1 && rulesToUnsnooze[0]) { + return deleteConfirmSingle(rulesToUnsnooze[0].name); + } + return deleteConfirmPlural(numberOfSelectedRules); + }, [rulesToUnsnooze, rulesToUnsnoozeFilter, numberOfSelectedRules]); + + if (isUnsnoozeModalOpen) { + return ( + + ); + } + if (isSnoozeModalOpen) { return ( @@ -138,11 +189,11 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { @@ -153,6 +204,7 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { ); } + return null; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx index 56293c01614d..cbf009acaa9a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx @@ -5,18 +5,18 @@ * 2.0. */ -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { + EuiConfirmModal, EuiModal, EuiModalHeader, + EuiModalHeaderTitle, EuiModalBody, EuiModalFooter, EuiSpacer, EuiButtonEmpty, - EuiModalHeaderTitle, - EuiConfirmModal, } from '@elastic/eui'; import { withBulkRuleOperations, @@ -49,24 +49,30 @@ const deleteConfirmSingle = (ruleName: string) => export type BulkSnoozeScheduleModalProps = { rulesToSchedule: RuleTableItem[]; + rulesToUnschedule: RuleTableItem[]; rulesToScheduleFilter?: string; + rulesToUnscheduleFilter?: string; numberOfSelectedRules?: number; onClose: () => void; onSave: () => void; - setIsLoading: (isLoading: boolean) => void; + setIsSchedulingRule: (isLoading: boolean) => void; + setIsUnschedulingRule: (isLoading: boolean) => void; onSearchPopulate?: (filter: string) => void; } & BulkOperationsComponentOpts; export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => { const { rulesToSchedule, + rulesToUnschedule, rulesToScheduleFilter, + rulesToUnscheduleFilter, numberOfSelectedRules = 0, onClose, onSave, bulkSnoozeRules, bulkUnsnoozeRules, - setIsLoading, + setIsSchedulingRule, + setIsUnschedulingRule, onSearchPopulate, } = props; @@ -76,8 +82,6 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => const { showToast } = useBulkEditResponse({ onSearchPopulate }); - const [showConfirmation, setShowConfirmation] = useState(false); - const isScheduleModalOpen = useMemo(() => { if (rulesToScheduleFilter) { return true; @@ -85,9 +89,16 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => return rulesToSchedule.length > 0; }, [rulesToSchedule, rulesToScheduleFilter]); + const isUnscheduleModalOpen = useMemo(() => { + if (rulesToUnscheduleFilter) { + return true; + } + return rulesToUnschedule.length > 0; + }, [rulesToUnschedule, rulesToUnscheduleFilter]); + const onAddSnoozeSchedule = async (schedule: SnoozeSchedule) => { onClose(); - setIsLoading(true); + setIsSchedulingRule(true); try { const response = await bulkSnoozeRules({ ids: rulesToSchedule.map((item) => item.id), @@ -100,18 +111,17 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => title: failureMessage, }); } - setIsLoading(false); + setIsSchedulingRule(false); onSave(); }; const onRemoveSnoozeSchedule = async () => { - setShowConfirmation(false); onClose(); - setIsLoading(true); + setIsUnschedulingRule(true); try { const response = await bulkUnsnoozeRules({ - ids: rulesToSchedule.map((item) => item.id), - filter: rulesToScheduleFilter, + ids: rulesToUnschedule.map((item) => item.id), + filter: rulesToUnscheduleFilter, scheduleIds: [], }); showToast(response, 'snoozeSchedule'); @@ -120,7 +130,7 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => title: failureMessage, }); } - setIsLoading(false); + setIsUnschedulingRule(false); onSave(); }; @@ -131,14 +141,11 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => return deleteConfirmPlural(numberOfSelectedRules); }, [rulesToSchedule, rulesToScheduleFilter, numberOfSelectedRules]); - if (showConfirmation) { + if (isUnscheduleModalOpen) { return ( { - setShowConfirmation(false); - onClose(); - }} + onCancel={onClose} onConfirm={onRemoveSnoozeSchedule} confirmButtonText={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.bulkDeleteConfirmButton', @@ -154,6 +161,7 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => )} buttonColor="danger" defaultFocusedButton="confirm" + data-test-subj="bulkRemoveScheduleConfirmationModal" /> ); } @@ -172,13 +180,12 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => setShowConfirmation(true)} + onCancelSchedules={onRemoveSnoozeSchedule} onClose={() => {}} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx index 3bde062935a8..6bfe953e2869 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx @@ -80,8 +80,14 @@ jest.mock('../../../../common/get_experimental_features', () => ({ const ruleTags = ['a', 'b', 'c', 'd']; -const { loadRuleTypes, updateAPIKey, loadRuleTags, bulkSnoozeRules, bulkUpdateAPIKey } = - jest.requireMock('../../../lib/rule_api'); +const { + loadRuleTypes, + updateAPIKey, + loadRuleTags, + bulkSnoozeRules, + bulkUnsnoozeRules, + bulkUpdateAPIKey, +} = jest.requireMock('../../../lib/rule_api'); const { loadRuleAggregationsWithKueryFilter } = jest.requireMock( '../../../lib/rule_api/aggregate_kuery_filter' ); @@ -1994,6 +2000,33 @@ describe.skip('Rules list bulk actions', () => { ); }); + it('can bulk unsnooze', async () => { + await setup(); + wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); + wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); + wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); + + // Unselect something to test filtering + wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); + + wrapper.find('[data-test-subj="bulkUnsnooze"]').first().simulate('click'); + + expect(wrapper.find('[data-test-subj="bulkUnsnoozeConfirmationModal"]').exists()).toBeTruthy(); + wrapper.find('[data-test-subj="confirmModalConfirmButton"]').first().simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(bulkUnsnoozeRules).toHaveBeenCalledWith( + expect.objectContaining({ + ids: [], + filter: 'NOT (alert.id: "alert:2")', + }) + ); + }); + it('can bulk add snooze schedule', async () => { await setup(); wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); @@ -2020,6 +2053,36 @@ describe.skip('Rules list bulk actions', () => { ); }); + it('can bulk remove snooze schedule', async () => { + await setup(); + wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); + wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); + wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); + + // Unselect something to test filtering + wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); + + wrapper.find('[data-test-subj="bulkRemoveSnoozeSchedule"]').first().simulate('click'); + + expect( + wrapper.find('[data-test-subj="bulkRemoveScheduleConfirmationModal"]').exists() + ).toBeTruthy(); + wrapper.find('[data-test-subj="confirmModalConfirmButton"]').first().simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(bulkUnsnoozeRules).toHaveBeenCalledWith( + expect.objectContaining({ + ids: [], + filter: 'NOT (alert.id: "alert:2")', + scheduleIds: [], + }) + ); + }); + it('can bulk update API key', async () => { await setup(); wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index a725a0e916c1..0061e56d4849 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -209,14 +209,22 @@ export const RulesList = ({ const [rulesToSnooze, setRulesToSnooze] = useState([]); const [rulesToSnoozeFilter, setRulesToSnoozeFilter] = useState(''); + const [rulesToUnsnooze, setRulesToUnsnooze] = useState([]); + const [rulesToUnsnoozeFilter, setRulesToUnsnoozeFilter] = useState(''); + const [rulesToSchedule, setRulesToSchedule] = useState([]); const [rulesToScheduleFilter, setRulesToScheduleFilter] = useState(''); + const [rulesToUnschedule, setRulesToUnschedule] = useState([]); + const [rulesToUnscheduleFilter, setRulesToUnscheduleFilter] = useState(''); + const [rulesToUpdateAPIKey, setRulesToUpdateAPIKey] = useState([]); const [rulesToUpdateAPIKeyFilter, setRulesToUpdateAPIKeyFilter] = useState(''); const [isSnoozingRules, setIsSnoozingRules] = useState(false); const [isSchedulingRules, setIsSchedulingRules] = useState(false); + const [isUnsnoozingRules, setIsUnsnoozingRules] = useState(false); + const [isUnschedulingRules, setIsUnschedulingRules] = useState(false); const [isUpdatingRuleAPIKeys, setIsUpdatingRuleAPIKeys] = useState(false); const hasAnyAuthorizedRuleType = useMemo(() => { @@ -597,11 +605,21 @@ export const RulesList = ({ setRulesToSnoozeFilter(''); }; + const clearRulesToUnsnooze = () => { + setRulesToUnsnooze([]); + setRulesToUnsnoozeFilter(''); + }; + const clearRulesToSchedule = () => { setRulesToSchedule([]); setRulesToScheduleFilter(''); }; + const clearRulesToUnschedule = () => { + setRulesToUnschedule([]); + setRulesToUnscheduleFilter(''); + }; + const clearRulesToUpdateAPIKey = () => { setRulesToUpdateAPIKey([]); setRulesToUpdateAPIKeyFilter(''); @@ -613,7 +631,9 @@ export const RulesList = ({ ruleTypesState.isLoading || isPerformingAction || isSnoozingRules || + isUnsnoozingRules || isSchedulingRules || + isUnschedulingRules || isUpdatingRuleAPIKeys ); }, [ @@ -621,7 +641,9 @@ export const RulesList = ({ ruleTypesState, isPerformingAction, isSnoozingRules, + isUnsnoozingRules, isSchedulingRules, + isUnschedulingRules, isUpdatingRuleAPIKeys, ]); @@ -903,14 +925,20 @@ export const RulesList = ({ setIsPerformingAction(false); }} isSnoozingRules={isSnoozingRules} + isUnsnoozingRules={isUnsnoozingRules} isSchedulingRules={isSchedulingRules} + isUnschedulingRules={isUnschedulingRules} isUpdatingRuleAPIKeys={isUpdatingRuleAPIKeys} setRulesToDelete={setRulesToDelete} setRulesToUpdateAPIKey={setRulesToUpdateAPIKey} setRulesToSnooze={setRulesToSnooze} + setRulesToUnsnooze={setRulesToUnsnooze} setRulesToSchedule={setRulesToSchedule} + setRulesToUnschedule={setRulesToUnschedule} setRulesToSnoozeFilter={setRulesToSnoozeFilter} + setRulesToUnsnoozeFilter={setRulesToUnsnoozeFilter} setRulesToScheduleFilter={setRulesToScheduleFilter} + setRulesToUnscheduleFilter={setRulesToUnscheduleFilter} setRulesToUpdateAPIKeyFilter={setRulesToUpdateAPIKeyFilter} /> @@ -983,13 +1011,19 @@ export const RulesList = ({ /> { clearRulesToSnooze(); + clearRulesToUnsnooze(); }} onSave={async () => { clearRulesToSnooze(); + clearRulesToUnsnooze(); onClearSelection(); await loadData(); }} @@ -997,14 +1031,19 @@ export const RulesList = ({ /> { clearRulesToSchedule(); + clearRulesToUnschedule(); }} onSave={async () => { clearRulesToSchedule(); + clearRulesToUnschedule(); onClearSelection(); await loadData(); }} From 7af3f3945c7a1f35048f338bb1110dbe28b6fff8 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Tue, 27 Sep 2022 14:51:55 -0400 Subject: [PATCH 037/185] Allow empty nextSyncConfig field in register connector request (#141986) --- .../server/routes/enterprise_search/connectors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts index c0dcc2dbdb0a..0aaf30ef126d 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts @@ -114,7 +114,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { connectorId: schema.string(), }), body: schema.object({ - nextSyncConfig: schema.string(), + nextSyncConfig: schema.maybe(schema.string()), }), }, }, From 504d8c4c700c8d8d1b2c7796b3915c2bc41b2c40 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 27 Sep 2022 14:54:15 -0400 Subject: [PATCH 038/185] [Security Solution][Endpoint] Generic hook to handle Response Actions API interactions (#141224) - New general hook (`useConsoleActionSubmitter()`) for handling response console commands that need to generate an endpoint response action - Refactored all Response Actions to use new hook - Fixed bug in `useIsMounted()` hook --- .../common/endpoint/schema/actions.ts | 2 + .../common/endpoint/types/actions.ts | 1 + .../artifact_list_page.test.tsx | 2 +- .../artifact_list_page/artifact_list_page.tsx | 4 +- .../components/artifact_delete_modal.test.ts | 2 +- .../components/artifact_flyout.test.tsx | 2 +- .../components/artifact_flyout.tsx | 12 +- .../hooks/use_with_artifact_list_data.ts | 8 +- .../management/components/console/types.ts | 2 +- .../get_processes_action.test.tsx | 4 +- .../get_processes_action.tsx | 257 +++++---------- .../components/endpoint_responder/hooks.tsx | 100 ------ .../use_console_action_submitter.test.tsx | 275 +++++++++++++++++ .../hooks/use_console_action_submitter.tsx | 292 ++++++++++++++++++ .../isolate_action.test.tsx | 6 +- .../endpoint_responder/isolate_action.tsx | 54 ++-- .../kill_process_action.test.tsx | 8 +- .../kill_process_action.tsx | 144 ++------- .../release_action.test.tsx | 6 +- .../endpoint_responder/release_action.tsx | 55 ++-- .../suspend_process_action.test.tsx | 8 +- .../suspend_process_action.tsx | 144 ++------- .../components/endpoint_responder/types.ts | 22 +- .../endpoint_responder/utils.test.ts | 4 +- .../components/endpoint_responder/utils.ts | 10 +- .../components/page_overlay/page_overlay.tsx | 8 +- .../public/management/hooks/use_is_mounted.ts | 26 -- .../{components/mocks.tsx => mocks/utils.ts} | 3 +- .../policy_artifacts_delete_modal.test.tsx | 2 +- .../translations/translations/fr-FR.json | 5 - .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 32 files changed, 806 insertions(+), 672 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/hooks/use_is_mounted.ts rename x-pack/plugins/security_solution/public/management/{components/mocks.tsx => mocks/utils.ts} (93%) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts index 08adac7c9ede..8b982e6e6f46 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts @@ -28,6 +28,8 @@ export const NoParametersRequestSchema = { body: schema.object({ ...BaseActionRequestSchema }), }; +export type BaseActionRequestBody = TypeOf; + export const KillOrSuspendProcessRequestSchema = { body: schema.object({ ...BaseActionRequestSchema, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index bcbdcfb3b66b..91eb10c5f45a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -253,6 +253,7 @@ export interface PendingActionsResponse { } export type PendingActionsRequestQuery = TypeOf; + export interface ActionDetails { /** The action id */ id: string; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx index 9160732e32b3..678033531225 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.test.tsx @@ -12,7 +12,7 @@ import { act, fireEvent, waitFor, waitForElementToBeRemoved } from '@testing-lib import userEvent from '@testing-library/user-event'; import type { ArtifactListPageRenderingSetup } from './mocks'; import { getArtifactListPageRenderingSetup } from './mocks'; -import { getDeferred } from '../mocks'; +import { getDeferred } from '../../mocks/utils'; jest.mock('../../../common/components/user_privileges'); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx index fc687282ccca..0586034d1555 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx @@ -11,6 +11,7 @@ import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-t import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import type { EuiFlyoutSize } from '@elastic/eui/src/components/flyout/flyout'; import { useLocation } from 'react-router-dom'; +import { useIsMounted } from '@kbn/securitysolution-hook-utils'; import type { ServerApiError } from '../../../common/types'; import { AdministrationListPage } from '../administration_list_page'; @@ -45,7 +46,6 @@ import { useToasts } from '../../../common/lib/kibana'; import { useMemoizedRouteState } from '../../common/hooks'; import { BackToExternalAppSecondaryButton } from '../back_to_external_app_secondary_button'; import { BackToExternalAppButton } from '../back_to_external_app_button'; -import { useIsMounted } from '../../hooks/use_is_mounted'; type ArtifactEntryCardType = typeof ArtifactEntryCard; @@ -221,7 +221,7 @@ export const ArtifactListPage = memo( ); const handleArtifactDeleteModalOnSuccess = useCallback(() => { - if (isMounted) { + if (isMounted()) { setSelectedItemForDelete(undefined); refetchListData(); } diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts index d0fb3e3c59df..57ea165f0b85 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.test.ts @@ -11,7 +11,7 @@ import type { ArtifactListPageRenderingSetup } from '../mocks'; import { getArtifactListPageRenderingSetup } from '../mocks'; import { act, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { getDeferred } from '../../mocks'; +import { getDeferred } from '../../../mocks/utils'; describe('When displaying the Delete artifact modal in the Artifact List Page', () => { let renderResult: ReturnType; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx index ee3ac4a907ee..5179bb7b76be 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx @@ -19,7 +19,7 @@ import type { trustedAppsAllHttpMocks } from '../../../mocks'; import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; import { entriesToConditionEntries } from '../../../../common/utils/exception_list_items/mappers'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { getDeferred } from '../../mocks'; +import { getDeferred } from '../../../mocks/utils'; jest.mock('../../../../common/components/user_privileges'); const useUserPrivileges = _useUserPrivileges as jest.Mock; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx index 4d9f53c73341..1261d01c7af4 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx @@ -24,6 +24,7 @@ import { import type { EuiFlyoutSize } from '@elastic/eui/src/components/flyout/flyout'; import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { useIsMounted } from '@kbn/securitysolution-hook-utils'; import { useUrlParams } from '../../../hooks/use_url_params'; import { useIsFlyoutOpened } from '../hooks/use_is_flyout_opened'; import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; @@ -39,7 +40,6 @@ import { useKibana, useToasts } from '../../../../common/lib/kibana'; import { createExceptionListItemForCreate } from '../../../../../common/endpoint/service/artifacts/utils'; import { useWithArtifactSubmitData } from '../hooks/use_with_artifact_submit_data'; import { useIsArtifactAllowedPerPolicyUsage } from '../hooks/use_is_artifact_allowed_per_policy_usage'; -import { useIsMounted } from '../../../hooks/use_is_mounted'; import { useGetArtifact } from '../../../hooks/artifacts'; import type { PolicyData } from '../../../../../common/endpoint/types'; @@ -271,7 +271,7 @@ export const ArtifactFlyout = memo( const handleFormComponentOnChange: ArtifactFormComponentProps['onChange'] = useCallback( ({ item: updatedItem, isValid }) => { - if (isMounted) { + if (isMounted()) { setFormState({ item: updatedItem, isValid, @@ -289,7 +289,7 @@ export const ArtifactFlyout = memo( : labels.flyoutCreateSubmitSuccess(result) ); - if (isMounted) { + if (isMounted()) { // Close the flyout // `undefined` will cause params to be dropped from url setUrlParams({ ...urlParams, itemId: undefined, show: undefined }, true); @@ -307,12 +307,12 @@ export const ArtifactFlyout = memo( submitHandler(formState.item, formMode) .then(handleSuccess) .catch((submitHandlerError) => { - if (isMounted) { + if (isMounted()) { setExternalSubmitHandlerError(submitHandlerError); } }) .finally(() => { - if (isMounted) { + if (isMounted()) { setExternalIsSubmittingData(false); } }); @@ -326,7 +326,7 @@ export const ArtifactFlyout = memo( useEffect(() => { if (isEditFlow && !hasItemDataForEdit && !error && isInitializing && !isLoadingItemForEdit) { fetchItemForEdit().then(({ data: editItemData }) => { - if (editItemData && isMounted) { + if (editItemData && isMounted()) { setFormState(createFormInitialState(apiClient.listId, editItemData)); } }); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts index 813e205b64c9..ddae258fef89 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts @@ -8,8 +8,8 @@ import { useEffect, useMemo, useState } from 'react'; import type { Pagination } from '@elastic/eui'; import { useQuery } from '@tanstack/react-query'; +import { useIsMounted } from '@kbn/securitysolution-hook-utils'; import type { ServerApiError } from '../../../../common/types'; -import { useIsMounted } from '../../../hooks/use_is_mounted'; import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; import { useUrlParams } from '../../../hooks/use_url_params'; import type { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; @@ -98,7 +98,7 @@ export const useWithArtifactListData = ( // Once we know if data exists, update the page initializing state. // This should only ever happen at most once; useEffect(() => { - if (isMounted) { + if (isMounted()) { if (isPageInitializing && !isLoadingDataExists) { setIsPageInitializing(false); } @@ -107,7 +107,7 @@ export const useWithArtifactListData = ( // Update the uiPagination once the query succeeds useEffect(() => { - if (isMounted && listData && !isLoadingListData && isSuccessListData) { + if (isMounted() && listData && !isLoadingListData && isSuccessListData) { setUiPagination((prevState) => { return { ...prevState, @@ -134,7 +134,7 @@ export const useWithArtifactListData = ( // >> Check if data exists again (which should return true useEffect(() => { if ( - isMounted && + isMounted() && !isLoadingListData && !isLoadingDataExists && !listDataError && diff --git a/x-pack/plugins/security_solution/public/management/components/console/types.ts b/x-pack/plugins/security_solution/public/management/components/console/types.ts index 2d863e7878be..5b90a18f27ce 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/types.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/types.ts @@ -168,7 +168,7 @@ export type CommandExecutionComponent< /** The arguments that could have been entered by the user */ TArgs extends SupportedArguments = any, /** Internal store for the Command execution */ - TStore extends object = Record, + TStore extends object = any, /** The metadata defined on the Command Definition */ TMeta = any > = ComponentType>; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx index 29b6fd044657..97689a790afa 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx @@ -129,7 +129,7 @@ describe('When using processes action from response actions console', () => { enterConsoleCommand(renderResult, 'processes'); await waitFor(() => { - expect(renderResult.getByTestId('getProcessesErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('getProcesses-actionFailure').textContent).toMatch( /error one \| error two/ ); }); @@ -145,7 +145,7 @@ describe('When using processes action from response actions console', () => { enterConsoleCommand(renderResult, 'processes'); await waitFor(() => { - expect(renderResult.getByTestId('performGetProcessesErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('getProcesses-apiFailure').textContent).toMatch( /this is an error/ ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.tsx index c778f03fcb5f..d7b05ae721ab 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.tsx @@ -5,21 +5,17 @@ * 2.0. */ -import React, { memo, useEffect, useMemo } from 'react'; +import React, { memo, useMemo } from 'react'; import styled from 'styled-components'; import { EuiBasicTable } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { useConsoleActionSubmitter } from './hooks/use_console_action_submitter'; import type { - ActionDetails, GetProcessesActionOutputContent, + ProcessesRequestBody, } from '../../../../common/endpoint/types'; -import { useGetActionDetails } from '../../hooks/endpoint/use_get_action_details'; -import type { EndpointCommandDefinitionMeta } from './types'; -import type { CommandExecutionComponentProps } from '../console/types'; import { useSendGetEndpointProcessesRequest } from '../../hooks/endpoint/use_send_get_endpoint_processes_request'; -import { ActionError } from './action_error'; +import type { ActionRequestComponentProps } from './types'; // @ts-expect-error TS2769 const StyledEuiBasicTable = styled(EuiBasicTable)` @@ -43,181 +39,90 @@ const StyledEuiBasicTable = styled(EuiBasicTable)` } `; -export const GetProcessesActionResult = memo< - CommandExecutionComponentProps< - { comment?: string }, - { - actionId?: string; - actionRequestSent?: boolean; - completedActionDetails?: ActionDetails; - apiError?: IHttpFetchError; - }, - EndpointCommandDefinitionMeta - > ->(({ command, setStore, store, status, setStatus, ResultComponent }) => { - const endpointId = command.commandDefinition?.meta?.endpointId; - const { actionId, completedActionDetails, apiError } = store; - - const isPending = status === 'pending'; - const isError = status === 'error'; - const actionRequestSent = Boolean(store.actionRequestSent); - - const { - mutate: getProcesses, - data: getProcessesData, - isSuccess: isGetProcessesSuccess, - error: processesActionRequestError, - } = useSendGetEndpointProcessesRequest(); - - const { data: actionDetails } = useGetActionDetails( - actionId ?? '-', - { - enabled: Boolean(actionId) && isPending, - refetchInterval: isPending ? 3000 : false, - } - ); - - // Send get processes request if not yet done - useEffect(() => { - if (!actionRequestSent && endpointId) { - getProcesses({ - endpoint_ids: [endpointId], - comment: command.args.args?.comment?.[0], - }); - - setStore((prevState) => { - return { ...prevState, actionRequestSent: true }; - }); - } - }, [actionRequestSent, command.args.args?.comment, endpointId, getProcesses, setStore]); +export const GetProcessesActionResult = memo( + ({ command, setStore, store, status, setStatus, ResultComponent }) => { + const endpointId = command.commandDefinition?.meta?.endpointId; + const actionCreator = useSendGetEndpointProcessesRequest(); + + const actionRequestBody = useMemo(() => { + return endpointId + ? { + endpoint_ids: [endpointId], + comment: command.args.args?.comment?.[0], + } + : undefined; + }, [command.args.args?.comment, endpointId]); + + const { result, actionDetails: completedActionDetails } = useConsoleActionSubmitter< + ProcessesRequestBody, + GetProcessesActionOutputContent + >({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator, + actionRequestBody, + dataTestSubj: 'getProcesses', + }); + + const columns = useMemo( + () => [ + { + field: 'user', + name: i18n.translate( + 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.user', + { defaultMessage: 'USER' } + ), + width: '10%', + }, + { + field: 'pid', + name: i18n.translate( + 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.pid', + { defaultMessage: 'PID' } + ), + width: '5%', + }, + { + field: 'entity_id', + name: i18n.translate( + 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.enityId', + { defaultMessage: 'ENTITY ID' } + ), + width: '30%', + }, + + { + field: 'command', + name: i18n.translate( + 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.command', + { defaultMessage: 'COMMAND' } + ), + width: '55%', + }, + ], + [] + ); - // If get processes request was created, store the action id if necessary - useEffect(() => { - if (isPending) { - if (isGetProcessesSuccess && actionId !== getProcessesData?.data.id) { - setStore((prevState) => { - return { ...prevState, actionId: getProcessesData?.data.id }; - }); - } else if (processesActionRequestError) { - setStatus('error'); - setStore((prevState) => { - return { ...prevState, apiError: processesActionRequestError }; - }); + const tableEntries = useMemo(() => { + if (endpointId) { + return completedActionDetails?.outputs?.[endpointId]?.content.entries ?? []; } - } - }, [ - actionId, - getProcessesData?.data.id, - processesActionRequestError, - isGetProcessesSuccess, - setStatus, - setStore, - isPending, - ]); - - useEffect(() => { - if (actionDetails?.data.isCompleted && isPending) { - setStatus('success'); - setStore((prevState) => { - return { - ...prevState, - completedActionDetails: actionDetails?.data, - }; - }); - } - }, [actionDetails?.data, setStatus, setStore, isPending]); - - const columns = useMemo( - () => [ - { - field: 'user', - name: i18n.translate( - 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.user', - { defaultMessage: 'USER' } - ), - width: '10%', - }, - { - field: 'pid', - name: i18n.translate( - 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.pid', - { defaultMessage: 'PID' } - ), - width: '5%', - }, - { - field: 'entity_id', - name: i18n.translate( - 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.enityId', - { defaultMessage: 'ENTITY ID' } - ), - width: '30%', - }, - - { - field: 'command', - name: i18n.translate( - 'xpack.securitySolution.endpointResponseActions.getProcesses.table.header.command', - { defaultMessage: 'COMMAND' } - ), - width: '55%', - }, - ], - [] - ); + return []; + }, [completedActionDetails?.outputs, endpointId]); - const tableEntries = useMemo(() => { - if (endpointId) { - return completedActionDetails?.outputs?.[endpointId]?.content.entries ?? []; + if (!completedActionDetails || !completedActionDetails.wasSuccessful) { + return result; } - return []; - }, [completedActionDetails?.outputs, endpointId]); - - // Show nothing if still pending - if (isPending) { - return ; - } - // Show errors if perform action fails - if (isError && apiError) { + // Show results return ( - - + + ); } - - // Show errors - if (completedActionDetails?.errors) { - return ( - - ); - } - - // Show results - return ( - - - - ); -}); +); GetProcessesActionResult.displayName = 'GetProcessesActionResult'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks.tsx deleted file mode 100644 index bbc390bb2ba5..000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks.tsx +++ /dev/null @@ -1,100 +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 { useEffect, useRef } from 'react'; -import { useIsMounted } from '../../hooks/use_is_mounted'; -import { useGetActionDetails } from '../../hooks/endpoint/use_get_action_details'; -import { ACTION_DETAILS_REFRESH_INTERVAL } from './constants'; -import type { ActionRequestState, ActionRequestComponentProps } from './types'; -import type { useSendIsolateEndpointRequest } from '../../hooks/endpoint/use_send_isolate_endpoint_request'; -import type { useSendReleaseEndpointRequest } from '../../hooks/endpoint/use_send_release_endpoint_request'; - -export const useUpdateActionState = ({ - actionRequestApi, - actionRequest, - command, - endpointId, - setStatus, - setStore, - isPending, -}: Pick & { - actionRequestApi: ReturnType< - typeof useSendIsolateEndpointRequest | typeof useSendReleaseEndpointRequest - >; - actionRequest?: ActionRequestState; - endpointId?: string; - isPending: boolean; -}) => { - const isMounted = useIsMounted(); - const actionRequestSent = Boolean(actionRequest?.requestSent); - const { data: actionDetails } = useGetActionDetails(actionRequest?.actionId ?? '-', { - enabled: Boolean(actionRequest?.actionId) && isPending, - refetchInterval: isPending ? ACTION_DETAILS_REFRESH_INTERVAL : false, - }); - - // keep a reference to track the console's mounted state - // in order to update the store and cause a re-render on action request API response - const latestIsMounted = useRef(false); - latestIsMounted.current = isMounted; - - // Create action request - useEffect(() => { - if (!actionRequestSent && endpointId && isMounted) { - const request: ActionRequestState = { - requestSent: true, - actionId: undefined, - }; - - actionRequestApi - .mutateAsync({ - endpoint_ids: [endpointId], - comment: command.args.args?.comment?.[0], - }) - .then((response) => { - request.actionId = response.data.id; - - if (latestIsMounted.current) { - setStore((prevState) => { - return { ...prevState, actionRequest: { ...request } }; - }); - } - }); - - setStore((prevState) => { - return { ...prevState, actionRequest: request }; - }); - } - }, [ - actionRequestApi, - actionRequestSent, - command.args.args?.comment, - endpointId, - isMounted, - setStore, - ]); - - useEffect(() => { - // update the console's mounted state ref - latestIsMounted.current = isMounted; - // set to false when unmounted/console is hidden - return () => { - latestIsMounted.current = false; - }; - }, [isMounted]); - - useEffect(() => { - if (actionDetails?.data.isCompleted && isPending) { - setStatus('success'); - setStore((prevState) => { - return { - ...prevState, - completedActionDetails: actionDetails.data, - }; - }); - } - }, [actionDetails?.data, actionDetails?.data.isCompleted, setStatus, setStore, isPending]); -}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.test.tsx new file mode 100644 index 000000000000..7ef506ea30f3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.test.tsx @@ -0,0 +1,275 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { + UseConsoleActionSubmitterOptions, + ConsoleActionSubmitter, + CommandResponseActionApiState, +} from './use_console_action_submitter'; +import { useConsoleActionSubmitter } from './use_console_action_submitter'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; +import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator'; +import React, { useState } from 'react'; +import type { CommandExecutionResultProps } from '../../console'; +import type { DeferredInterface } from '../../../mocks/utils'; +import { getDeferred } from '../../../mocks/utils'; +import type { ActionDetails } from '../../../../../common/endpoint/types'; +import { act, waitFor } from '@testing-library/react'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; + +describe('When using `useConsoleActionSubmitter()` hook', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let renderArgs: UseConsoleActionSubmitterOptions; + let updateHookRenderArgs: () => void; + let hookRenderResultStorage: jest.Mock<(args: ConsoleActionSubmitter) => void>; + let releaseSuccessActionRequestApiResponse: DeferredInterface['resolve']; + let releaseFailedActionRequestApiResponse: DeferredInterface['reject']; + let apiMocks: ReturnType; + + const ActionSubmitterTestComponent = () => { + const [hookOptions, setHookOptions] = useState(renderArgs); + + updateHookRenderArgs = () => { + new Promise((r) => { + setTimeout(r, 1); + }).then(() => { + setHookOptions({ + ...renderArgs, + }); + }); + }; + + const { result, actionDetails } = useConsoleActionSubmitter(hookOptions); + + hookRenderResultStorage({ result, actionDetails }); + + return
    {result}
    ; + }; + + const getOutputTextContent = (): string => { + return renderResult.getByTestId('testContainer').textContent ?? ''; + }; + + beforeEach(() => { + const { render: renderComponent, coreStart } = createAppRootMockRenderer(); + const actionGenerator = new EndpointActionGenerator(); + const deferred = getDeferred(); + + apiMocks = responseActionsHttpMocks(coreStart.http); + + hookRenderResultStorage = jest.fn(); + releaseSuccessActionRequestApiResponse = () => + deferred.resolve(actionGenerator.generateActionDetails({ id: '123' })); + releaseFailedActionRequestApiResponse = deferred.reject; + + let status: UseConsoleActionSubmitterOptions['status'] = 'pending'; + let commandStore: CommandResponseActionApiState = {}; + + renderArgs = { + dataTestSubj: 'test', + actionRequestBody: { + endpoint_ids: ['123'], + }, + actionCreator: { + mutateAsync: jest.fn(async () => { + return { + data: await deferred.promise, + }; + }), + } as unknown as UseConsoleActionSubmitterOptions['actionCreator'], + get status() { + return status; + }, + setStatus: jest.fn((newStatus) => { + status = newStatus; + updateHookRenderArgs(); + }), + get store() { + return commandStore; + }, + setStore: jest.fn((newStoreOrCallback: object | ((prevStore: object) => object)) => { + if (typeof newStoreOrCallback === 'function') { + commandStore = newStoreOrCallback(commandStore); + } else { + commandStore = newStoreOrCallback; + } + + updateHookRenderArgs(); + }), + ResultComponent: jest.fn( + ({ children, showAs, 'data-test-subj': dataTestSubj }: CommandExecutionResultProps) => { + return ( +
    + {children} +
    + ); + } + ), + }; + + render = () => { + renderResult = renderComponent(); + return renderResult; + }; + }); + + afterEach(() => { + renderResult.unmount(); + }); + + it('should return expected interface while its still pending', () => { + render(); + + expect(hookRenderResultStorage).toHaveBeenLastCalledWith({ + result: expect.anything(), + action: undefined, + }); + + expect(renderResult.getByTestId('test-pending')).not.toBeNull(); + }); + + it('should update command state when request is sent', () => { + render(); + + expect(renderArgs.store?.actionApiState?.request.sent).toBe(true); + expect(renderArgs.store?.actionApiState?.request.actionId).toBe(undefined); + }); + + it('should store the action id when action request api is successful', async () => { + render(); + + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + expect(renderArgs.store?.actionApiState?.request.actionId).toBe('123'); + }); + }); + + it('should store action request api error', async () => { + render(); + const error = new Error('oh oh. request failed'); + + act(() => { + releaseFailedActionRequestApiResponse(error); + }); + + await waitFor(() => { + expect(renderArgs.store?.actionApiState?.request.actionId).toBe(undefined); + expect(renderArgs.store?.actionApiState?.request.error).toBe(error); + }); + + await waitFor(() => { + expect(getOutputTextContent()).toEqual( + 'The following error was encountered:oh oh. request failed' + ); + }); + }); + + it('should still store the action id if component is unmounted while action request API is in flight', async () => { + render(); + renderResult.unmount(); + + expect(renderArgs.store.actionApiState?.request.sent).toBe(true); + + const requestState = renderArgs.store.actionApiState?.request; + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + // this check just ensure that we mutated the state when the api returned success instead of + // dispatching a `setStore()`. + expect(renderArgs.store.actionApiState?.request === requestState).toBe(true); + + expect(renderArgs.store.actionApiState?.request.actionId).toEqual('123'); + }); + }); + + it('should call action details api once we have an action id', async () => { + render(); + + expect(apiMocks.responseProvider.actionDetails).not.toHaveBeenCalled(); + + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledWith({ + path: '/api/endpoint/action/123', + }); + }); + }); + + it('should continue to show pending message until action completes', async () => { + apiMocks.responseProvider.actionDetails.mockImplementation(() => { + return { + data: new EndpointActionGenerator().generateActionDetails({ + id: '123', + isCompleted: false, + }), + }; + }); + render(); + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledWith({ + path: '/api/endpoint/action/123', + }); + }); + + expect(renderResult.getByTestId('test-pending')).not.toBeNull(); + + expect(hookRenderResultStorage).toHaveBeenLastCalledWith({ + result: expect.anything(), + actionDetails: undefined, + }); + }); + + it('should store action details api error', async () => { + const error = new Error('on oh. getting action details failed'); + apiMocks.responseProvider.actionDetails.mockImplementation(() => { + throw error; + }); + + render(); + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledWith({ + path: '/api/endpoint/action/123', + }); + }); + + expect(renderArgs.store.actionApiState?.actionDetailsError).toBe(error); + + expect(renderResult.getByTestId('test-apiFailure').textContent).toEqual( + 'The following error was encountered:on oh. getting action details failed' + ); + }); + + it('should store action details once action completes', async () => { + const actionDetails = new EndpointActionGenerator().generateActionDetails({ id: '123' }); + apiMocks.responseProvider.actionDetails.mockReturnValue({ data: actionDetails }); + + render(); + releaseSuccessActionRequestApiResponse(); + + await waitFor(() => { + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledWith({ + path: '/api/endpoint/action/123', + }); + }); + + expect(renderArgs.store.actionApiState?.actionDetails).toBe(actionDetails); + expect(hookRenderResultStorage).toHaveBeenLastCalledWith({ + result: expect.anything(), + actionDetails, + }); + + expect(renderResult.getByTestId('test-success').textContent).toEqual(''); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.tsx new file mode 100644 index 000000000000..7183b5cc61ef --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/hooks/use_console_action_submitter.tsx @@ -0,0 +1,292 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { useEffect, useMemo } from 'react'; +import type { UseMutationResult } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useIsMounted } from '@kbn/securitysolution-hook-utils'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; +import type { BaseActionRequestBody } from '../../../../../common/endpoint/schema/actions'; +import { ActionSuccess } from '../action_success'; +import { ActionError } from '../action_error'; +import { FormattedError } from '../../formatted_error'; +import { useGetActionDetails } from '../../../hooks/endpoint/use_get_action_details'; +import { ACTION_DETAILS_REFRESH_INTERVAL } from '../constants'; +import type { + ActionDetails, + Immutable, + ResponseActionApiResponse, +} from '../../../../../common/endpoint/types'; +import type { CommandExecutionComponentProps } from '../../console'; + +export interface ConsoleActionSubmitter { + /** + * The ui to be returned to the console. This UI will display different states of the action, + * including pending, error conditions and generic success messages. + */ + result: JSX.Element; + actionDetails: Immutable> | undefined; +} + +/** + * Command store state for response action api state. + */ +export interface CommandResponseActionApiState { + actionApiState?: { + request: { + sent: boolean; + actionId: string | undefined; + error: IHttpFetchError | undefined; + }; + actionDetails: ActionDetails | undefined; + actionDetailsError: IHttpFetchError | undefined; + }; +} + +export interface UseConsoleActionSubmitterOptions< + TReqBody extends BaseActionRequestBody = BaseActionRequestBody, + TActionOutputContent extends object = object +> extends Pick< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + CommandExecutionComponentProps>, + 'ResultComponent' | 'setStore' | 'store' | 'status' | 'setStatus' + > { + actionCreator: UseMutationResult; + /** + * The API request body. If `undefined`, then API will not be called. + */ + actionRequestBody: TReqBody | undefined; + + dataTestSubj?: string; +} + +/** + * generic hook for use with Response Action commands. It will create the action, store its ID and + * continuously pull the Action's Details until it completes. It handles all aspects of UI display + * for the different states of the command (pending -> success/failure) + * + * @param actionCreator + * @param actionRequestBody + * @param setStatus + * @param status + * @param setStore + * @param store + * @param ResultComponent + * @param dataTestSubj + */ +export const useConsoleActionSubmitter = < + TReqBody extends BaseActionRequestBody = BaseActionRequestBody, + TActionOutputContent extends object = object +>({ + actionCreator, + actionRequestBody, + setStatus, + status, + setStore, + store, + ResultComponent, + dataTestSubj, +}: UseConsoleActionSubmitterOptions< + TReqBody, + TActionOutputContent +>): ConsoleActionSubmitter => { + const isMounted = useIsMounted(); + const getTestId = useTestIdGenerator(dataTestSubj); + const isPending = status === 'pending'; + + const currentActionState = useMemo< + Immutable>['actionApiState']> + >( + () => + store.actionApiState ?? { + request: { + sent: false, + error: undefined, + actionId: undefined, + }, + actionDetails: undefined, + actionDetailsError: undefined, + }, + [store.actionApiState] + ); + + const { actionDetails, actionDetailsError } = currentActionState; + const { + actionId, + sent: actionRequestSent, + error: actionRequestError, + } = currentActionState.request; + + const { data: apiActionDetailsResponse, error: apiActionDetailsError } = + useGetActionDetails(actionId ?? '-', { + enabled: Boolean(actionId) && isPending, + refetchInterval: isPending ? ACTION_DETAILS_REFRESH_INTERVAL : false, + }); + + // Create the action request if not yet done + useEffect(() => { + if (!actionRequestSent && actionRequestBody && isMounted()) { + const updatedRequestState: Required< + CommandResponseActionApiState + >['actionApiState']['request'] = { + ...( + currentActionState as Required< + CommandResponseActionApiState + >['actionApiState'] + ).request, + sent: true, + }; + + // The object defined above (`updatedRequestState`) is saved to the command state right away. + // the creation of the Action request (below) will mutate this object to store the Action ID + // once the API response is received. We do this to ensure that the action is not created more + // than once if the user happens to close the console prior to the response being returned. + // Once a response is received, we check if the component is mounted, and if so, then we send + // another update to the command store which will cause it to re-render and start checking for + // action completion. + actionCreator + .mutateAsync(actionRequestBody) + .then((response) => { + updatedRequestState.actionId = response.data.id; + }) + .catch((err) => { + updatedRequestState.error = err; + }) + .finally(() => { + // If the component is mounted, then set the store with the updated data (causes a rerender) + if (isMounted()) { + setStore((prevState) => { + return { + ...prevState, + actionApiState: { + ...(prevState.actionApiState ?? currentActionState), + request: { ...updatedRequestState }, + }, + }; + }); + } + }); + + setStore((prevState) => { + return { + ...prevState, + actionApiState: { + ...(prevState.actionApiState ?? currentActionState), + request: updatedRequestState, + }, + }; + }); + } + }, [ + actionCreator, + actionRequestBody, + actionRequestSent, + currentActionState, + isMounted, + setStore, + ]); + + // If an error was returned while attempting to create the action request, + // then set command status to error + useEffect(() => { + if (actionRequestError && isPending) { + setStatus('error'); + } + }, [actionRequestError, isPending, setStatus]); + + // If an error was return by the Action Details API, then store it and set the status to error + useEffect(() => { + if (apiActionDetailsError && isPending) { + setStatus('error'); + setStore((prevState) => { + return { + ...prevState, + actionApiState: { + ...(prevState.actionApiState ?? currentActionState), + actionDetails: undefined, + actionDetailsError: apiActionDetailsError, + }, + }; + }); + } + }, [apiActionDetailsError, currentActionState, isPending, setStatus, setStore]); + + // If the action details indicates complete, then update the action's console state and set the status to success + useEffect(() => { + if (apiActionDetailsResponse?.data.isCompleted && isPending) { + setStatus(apiActionDetailsResponse?.data.wasSuccessful ? 'success' : 'error'); + setStore((prevState) => { + return { + ...prevState, + actionApiState: { + ...(prevState.actionApiState ?? currentActionState), + actionDetails: apiActionDetailsResponse.data, + }, + // Unclear why I needed to cast this here. For some reason the `ActionDetails['outputs']` is + // reporting a type error for the `content` property, although the types seem to line up. + } as typeof prevState; + }); + } + }, [apiActionDetailsResponse, currentActionState, isPending, setStatus, setStore]); + + // Calculate the action's UI result based on the different API responses + const result = useMemo(() => { + if (isPending) { + return ; + } + + const apiError = actionRequestError || actionDetailsError; + + if (apiError) { + return ( + + + + + ); + } + + if (actionDetails) { + // Response action failures + if (actionDetails.errors) { + return ( + + ); + } + + return ( + + ); + } + + return <>; + }, [ + isPending, + actionRequestError, + actionDetailsError, + actionDetails, + ResultComponent, + getTestId, + ]); + + return { + result, + actionDetails: currentActionState.actionDetails, + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx index 63e05d420dea..110ddbb53b1b 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx @@ -16,9 +16,9 @@ import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_a import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; import { enterConsoleCommand } from '../console/mocks'; import { waitFor } from '@testing-library/react'; -import { getDeferred } from '../mocks'; import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; +import { getDeferred } from '../../mocks/utils'; describe('When using isolate action from response actions console', () => { let render: ( @@ -115,7 +115,7 @@ describe('When using isolate action from response actions console', () => { enterConsoleCommand(renderResult, 'isolate'); await waitFor(() => { - expect(renderResult.getByTestId('isolateSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('isolate-success')).toBeTruthy(); }); }); @@ -130,7 +130,7 @@ describe('When using isolate action from response actions console', () => { enterConsoleCommand(renderResult, 'isolate'); await waitFor(() => { - expect(renderResult.getByTestId('isolateErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('isolate-actionFailure').textContent).toMatch( /error one \| error two/ ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.tsx index ec11d022650c..8df7692cf3ac 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.tsx @@ -5,47 +5,37 @@ * 2.0. */ -import React, { memo } from 'react'; +import { memo, useMemo } from 'react'; +import { useConsoleActionSubmitter } from './hooks/use_console_action_submitter'; import type { ActionRequestComponentProps } from './types'; import { useSendIsolateEndpointRequest } from '../../hooks/endpoint/use_send_isolate_endpoint_request'; -import { ActionError } from './action_error'; -import { useUpdateActionState } from './hooks'; export const IsolateActionResult = memo( ({ command, setStore, store, status, setStatus, ResultComponent }) => { - const endpointId = command.commandDefinition?.meta?.endpointId; - const { completedActionDetails, actionRequest } = store; - const isPending = status === 'pending'; const isolateHostApi = useSendIsolateEndpointRequest(); - useUpdateActionState({ - actionRequestApi: isolateHostApi, - actionRequest, - command, - endpointId, - setStatus, - setStore, - isPending, - }); - - // Show nothing if still pending - if (isPending) { - return ; - } + const actionRequestBody = useMemo(() => { + const endpointId = command.commandDefinition?.meta?.endpointId; + const comment = command.args.args?.comment?.[0]; - // Show errors - if (completedActionDetails?.errors) { - return ( - - ); - } + return endpointId + ? { + endpoint_ids: [endpointId], + comment, + } + : undefined; + }, [command.args.args?.comment, command.commandDefinition?.meta?.endpointId]); - // Show Success - return ; + return useConsoleActionSubmitter({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator: isolateHostApi, + actionRequestBody, + dataTestSubj: 'isolate', + }).result; } ); IsolateActionResult.displayName = 'IsolateActionResult'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx index 827a4d619175..f888df2099b1 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx @@ -195,7 +195,7 @@ describe('When using the kill-process action from response actions console', () enterConsoleCommand(renderResult, 'kill-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('killProcessSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('killProcess-success')).toBeTruthy(); }); }); @@ -204,7 +204,7 @@ describe('When using the kill-process action from response actions console', () enterConsoleCommand(renderResult, 'kill-process --entityId 123wer'); await waitFor(() => { - expect(renderResult.getByTestId('killProcessSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('killProcess-success')).toBeTruthy(); }); }); @@ -219,7 +219,7 @@ describe('When using the kill-process action from response actions console', () enterConsoleCommand(renderResult, 'kill-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('killProcessErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('killProcess-actionFailure').textContent).toMatch( /error one \| error two/ ); }); @@ -234,7 +234,7 @@ describe('When using the kill-process action from response actions console', () enterConsoleCommand(renderResult, 'kill-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('killProcessAPIErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('killProcess-apiFailure').textContent).toMatch( /this is an error/ ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.tsx index 3e4dcf61d169..bf501c31b9e8 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.tsx @@ -5,130 +5,40 @@ * 2.0. */ -import React, { memo, useEffect } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { memo, useMemo } from 'react'; +import type { KillOrSuspendProcessRequestBody } from '../../../../common/endpoint/types'; import { parsedPidOrEntityIdParameter } from './utils'; -import { ActionSuccess } from './action_success'; -import type { - ActionDetails, - KillProcessActionOutputContent, -} from '../../../../common/endpoint/types'; -import { useGetActionDetails } from '../../hooks/endpoint/use_get_action_details'; -import type { EndpointCommandDefinitionMeta } from './types'; import { useSendKillProcessRequest } from '../../hooks/endpoint/use_send_kill_process_endpoint_request'; -import type { CommandExecutionComponentProps } from '../console/types'; -import { ActionError } from './action_error'; -import { ACTION_DETAILS_REFRESH_INTERVAL } from './constants'; +import type { ActionRequestComponentProps } from './types'; +import { useConsoleActionSubmitter } from './hooks/use_console_action_submitter'; export const KillProcessActionResult = memo< - CommandExecutionComponentProps< - { comment?: string; pid?: string; entityId?: string }, - { - actionId?: string; - actionRequestSent?: boolean; - completedActionDetails?: ActionDetails; - apiError?: IHttpFetchError; - }, - EndpointCommandDefinitionMeta - > + ActionRequestComponentProps<{ pid?: string[]; entityId?: string[] }> >(({ command, setStore, store, status, setStatus, ResultComponent }) => { - const endpointId = command.commandDefinition?.meta?.endpointId; - const { actionId, completedActionDetails, apiError } = store; - const isPending = status === 'pending'; - const isError = status === 'error'; - const actionRequestSent = Boolean(store.actionRequestSent); + const actionCreator = useSendKillProcessRequest(); - const { mutate, data, isSuccess, error } = useSendKillProcessRequest(); - - const { data: actionDetails } = useGetActionDetails( - actionId ?? '-', - { - enabled: Boolean(actionId) && isPending, - refetchInterval: isPending ? ACTION_DETAILS_REFRESH_INTERVAL : false, - } - ); - - // Send Kill request if not yet done - useEffect(() => { + const actionRequestBody = useMemo(() => { + const endpointId = command.commandDefinition?.meta?.endpointId; const parameters = parsedPidOrEntityIdParameter(command.args.args); - if (!actionRequestSent && endpointId && parameters) { - mutate({ - endpoint_ids: [endpointId], - comment: command.args.args?.comment?.[0], - parameters, - }); - setStore((prevState) => { - return { ...prevState, actionRequestSent: true }; - }); - } - }, [actionRequestSent, command.args.args, endpointId, mutate, setStore]); - - // If kill-process request was created, store the action id if necessary - useEffect(() => { - if (isPending) { - if (isSuccess && actionId !== data.data.id) { - setStore((prevState) => { - return { ...prevState, actionId: data.data.id }; - }); - } else if (error) { - setStatus('error'); - setStore((prevState) => { - return { ...prevState, apiError: error }; - }); - } - } - }, [actionId, data?.data.id, isSuccess, error, setStore, setStatus, isPending]); - - useEffect(() => { - if (actionDetails?.data.isCompleted && isPending) { - setStatus('success'); - setStore((prevState) => { - return { - ...prevState, - completedActionDetails: actionDetails.data, - }; - }); - } - }, [actionDetails?.data, setStatus, setStore, isPending]); - - // Show API errors if perform action fails - if (isError && apiError) { - return ( - - - - ); - } - - // Show nothing if still pending - if (isPending || !completedActionDetails) { - return ; - } - - // Show errors - if (completedActionDetails?.errors) { - return ( - - ); - } - - // Show Success - return ( - - ); + return endpointId + ? { + endpoint_ids: [endpointId], + comment: command.args.args?.comment?.[0], + parameters, + } + : undefined; + }, [command.args.args, command.commandDefinition?.meta?.endpointId]); + + return useConsoleActionSubmitter({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator, + actionRequestBody, + dataTestSubj: 'killProcess', + }).result; }); KillProcessActionResult.displayName = 'KillProcessActionResult'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx index 19e3be94469e..d1c1ea264f86 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx @@ -16,9 +16,9 @@ import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_a import { enterConsoleCommand } from '../console/mocks'; import { waitFor } from '@testing-library/react'; import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import { getDeferred } from '../mocks'; import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; +import { getDeferred } from '../../mocks/utils'; describe('When using the release action from response actions console', () => { let render: ( @@ -116,7 +116,7 @@ describe('When using the release action from response actions console', () => { enterConsoleCommand(renderResult, 'release'); await waitFor(() => { - expect(renderResult.getByTestId('releaseSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('release-success')).toBeTruthy(); }); }); @@ -131,7 +131,7 @@ describe('When using the release action from response actions console', () => { enterConsoleCommand(renderResult, 'release'); await waitFor(() => { - expect(renderResult.getByTestId('releaseErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('release-actionFailure').textContent).toMatch( /error one \| error two/ ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.tsx index f789b4867132..9b0f371ca003 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.tsx @@ -5,48 +5,37 @@ * 2.0. */ -import React, { memo } from 'react'; +import { memo, useMemo } from 'react'; import type { ActionRequestComponentProps } from './types'; import { useSendReleaseEndpointRequest } from '../../hooks/endpoint/use_send_release_endpoint_request'; -import { ActionError } from './action_error'; -import { useUpdateActionState } from './hooks'; +import { useConsoleActionSubmitter } from './hooks/use_console_action_submitter'; export const ReleaseActionResult = memo( ({ command, setStore, store, status, setStatus, ResultComponent }) => { - const endpointId = command.commandDefinition?.meta?.endpointId; - const { completedActionDetails, actionRequest } = store; - const isPending = status === 'pending'; - const releaseHostApi = useSendReleaseEndpointRequest(); - useUpdateActionState({ - actionRequestApi: releaseHostApi, - actionRequest, - command, - endpointId, - setStatus, - setStore, - isPending, - }); - - // Show nothing if still pending - if (isPending) { - return ; - } + const actionRequestBody = useMemo(() => { + const endpointId = command.commandDefinition?.meta?.endpointId; + const comment = command.args.args?.comment?.[0]; - // Show errors - if (completedActionDetails?.errors) { - return ( - - ); - } + return endpointId + ? { + endpoint_ids: [endpointId], + comment, + } + : undefined; + }, [command.args.args?.comment, command.commandDefinition?.meta?.endpointId]); - // Show Success - return ; + return useConsoleActionSubmitter({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator: releaseHostApi, + actionRequestBody, + dataTestSubj: 'release', + }).result; } ); ReleaseActionResult.displayName = 'ReleaseActionResult'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx index 9446fb5dcba6..7479e52edfb0 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx @@ -186,7 +186,7 @@ describe('When using the suspend-process action from response actions console', enterConsoleCommand(renderResult, 'suspend-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('suspendProcessSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('suspendProcess-success')).toBeTruthy(); }); }); @@ -195,7 +195,7 @@ describe('When using the suspend-process action from response actions console', enterConsoleCommand(renderResult, 'suspend-process --entityId 123wer'); await waitFor(() => { - expect(renderResult.getByTestId('suspendProcessSuccessCallout')).toBeTruthy(); + expect(renderResult.getByTestId('suspendProcess-success')).toBeTruthy(); }); }); @@ -210,7 +210,7 @@ describe('When using the suspend-process action from response actions console', enterConsoleCommand(renderResult, 'suspend-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('suspendProcessErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('suspendProcess-actionFailure').textContent).toMatch( /error one \| error two/ ); }); @@ -225,7 +225,7 @@ describe('When using the suspend-process action from response actions console', enterConsoleCommand(renderResult, 'suspend-process --pid 123'); await waitFor(() => { - expect(renderResult.getByTestId('suspendProcessAPIErrorCallout').textContent).toMatch( + expect(renderResult.getByTestId('suspendProcess-apiFailure').textContent).toMatch( /this is an error/ ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.tsx index a60e7eb6bd65..f8401a81fa11 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.tsx @@ -5,130 +5,46 @@ * 2.0. */ -import React, { memo, useEffect } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { memo, useMemo } from 'react'; import { parsedPidOrEntityIdParameter } from './utils'; -import { ActionSuccess } from './action_success'; import type { - ActionDetails, SuspendProcessActionOutputContent, + KillOrSuspendProcessRequestBody, } from '../../../../common/endpoint/types'; -import { useGetActionDetails } from '../../hooks/endpoint/use_get_action_details'; -import type { EndpointCommandDefinitionMeta } from './types'; import { useSendSuspendProcessRequest } from '../../hooks/endpoint/use_send_suspend_process_endpoint_request'; -import type { CommandExecutionComponentProps } from '../console/types'; -import { ActionError } from './action_error'; -import { ACTION_DETAILS_REFRESH_INTERVAL } from './constants'; +import type { ActionRequestComponentProps } from './types'; +import { useConsoleActionSubmitter } from './hooks/use_console_action_submitter'; export const SuspendProcessActionResult = memo< - CommandExecutionComponentProps< - { comment?: string; pid?: string; entityId?: string }, - { - actionId?: string; - actionRequestSent?: boolean; - completedActionDetails?: ActionDetails; - apiError?: IHttpFetchError; - }, - EndpointCommandDefinitionMeta - > + ActionRequestComponentProps<{ pid?: string[]; entityId?: string[] }> >(({ command, setStore, store, status, setStatus, ResultComponent }) => { - const endpointId = command.commandDefinition?.meta?.endpointId; - const { actionId, completedActionDetails, apiError } = store; - const isPending = status === 'pending'; - const isError = status === 'error'; - const actionRequestSent = Boolean(store.actionRequestSent); + const actionCreator = useSendSuspendProcessRequest(); - const { mutate, data, isSuccess, error } = useSendSuspendProcessRequest(); - - const { data: actionDetails } = useGetActionDetails( - actionId ?? '-', - { - enabled: Boolean(actionId) && isPending, - refetchInterval: isPending ? ACTION_DETAILS_REFRESH_INTERVAL : false, - } - ); - - // Send Suspend request if not yet done - useEffect(() => { + const actionRequestBody = useMemo(() => { + const endpointId = command.commandDefinition?.meta?.endpointId; const parameters = parsedPidOrEntityIdParameter(command.args.args); - if (!actionRequestSent && endpointId && parameters) { - mutate({ - endpoint_ids: [endpointId], - comment: command.args.args?.comment?.[0], - parameters, - }); - setStore((prevState) => { - return { ...prevState, actionRequestSent: true }; - }); - } - }, [actionRequestSent, command.args.args, endpointId, mutate, setStore]); - - // If suspend-process request was created, store the action id if necessary - useEffect(() => { - if (isPending) { - if (isSuccess && actionId !== data.data.id) { - setStore((prevState) => { - return { ...prevState, actionId: data.data.id }; - }); - } else if (error) { - setStatus('error'); - setStore((prevState) => { - return { ...prevState, apiError: error }; - }); - } - } - }, [actionId, data?.data.id, isSuccess, error, setStore, setStatus, isPending]); - - useEffect(() => { - if (actionDetails?.data.isCompleted && isPending) { - setStatus('success'); - setStore((prevState) => { - return { - ...prevState, - completedActionDetails: actionDetails.data, - }; - }); - } - }, [actionDetails?.data, setStatus, setStore, isPending]); - - // Show API errors if perform action fails - if (isError && apiError) { - return ( - - - - ); - } - - // Show nothing if still pending - if (isPending || !completedActionDetails) { - return ; - } - - // Show errors - if (completedActionDetails?.errors) { - return ( - - ); - } - - // Show Success - return ( - - ); + return endpointId + ? { + endpoint_ids: [endpointId], + comment: command.args.args?.comment?.[0], + parameters, + } + : undefined; + }, [command.args.args, command.commandDefinition?.meta?.endpointId]); + + return useConsoleActionSubmitter< + KillOrSuspendProcessRequestBody, + SuspendProcessActionOutputContent + >({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator, + actionRequestBody, + dataTestSubj: 'suspendProcess', + }).result; }); SuspendProcessActionResult.displayName = 'SuspendProcessActionResult'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/types.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/types.ts index 34c306ed0a11..92cc3e8c9017 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/types.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/types.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { CommandResponseActionApiState } from './hooks/use_console_action_submitter'; import type { ManagedConsoleExtensionComponentProps } from '../console'; -import type { ActionDetails, HostMetadata } from '../../../../common/endpoint/types'; +import type { HostMetadata } from '../../../../common/endpoint/types'; import type { CommandExecutionComponentProps } from '../console/types'; export interface EndpointCommandDefinitionMeta { @@ -17,16 +18,9 @@ export type EndpointResponderExtensionComponentProps = ManagedConsoleExtensionCo endpoint: HostMetadata; }>; -export interface ActionRequestState { - requestSent: boolean; - actionId?: string; -} - -export type ActionRequestComponentProps = CommandExecutionComponentProps< - { comment?: string }, - { - actionRequest?: ActionRequestState; - completedActionDetails?: ActionDetails; - }, - EndpointCommandDefinitionMeta ->; +export type ActionRequestComponentProps = + CommandExecutionComponentProps< + { comment?: string } & TArgs, + CommandResponseActionApiState, + EndpointCommandDefinitionMeta + >; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.test.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.test.ts index ab84e9de959f..48b6753645c9 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.test.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.test.ts @@ -19,9 +19,9 @@ describe('Endpoint Responder - Utilities', () => { expect(parameters).toEqual({ entity_id: '123qwe' }); }); - it('should return undefined if no params are defined', () => { + it('should return entity id with emtpy string if no params are defined', () => { const parameters = parsedPidOrEntityIdParameter({}); - expect(parameters).toEqual(undefined); + expect(parameters).toEqual({ entity_id: '' }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.ts index 9ebcd090bd2a..0b8e59d0353f 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/utils.ts @@ -4,17 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { EndpointActionDataParameterTypes } from '../../../../common/endpoint/types'; +import type { ResponseActionParametersWithPidOrEntityId } from '../../../../common/endpoint/types'; export const parsedPidOrEntityIdParameter = (parameters: { pid?: string[]; entityId?: string[]; -}): EndpointActionDataParameterTypes => { +}): ResponseActionParametersWithPidOrEntityId => { if (parameters.pid) { return { pid: Number(parameters.pid[0]) }; - } else if (parameters.entityId) { - return { entity_id: parameters.entityId[0] }; } - return undefined; + return { + entity_id: parameters?.entityId?.[0] ?? '', + }; }; diff --git a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx index b5c7629b76aa..2d3a9c9cb7f0 100644 --- a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx +++ b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx @@ -13,6 +13,7 @@ import classnames from 'classnames'; import { useLocation } from 'react-router-dom'; import type { EuiPortalProps } from '@elastic/eui/src/components/portal/portal'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; +import { useIsMounted } from '@kbn/securitysolution-hook-utils'; import { useHasFullScreenContent } from '../../../common/containers/use_full_screen'; import { FULL_SCREEN_CONTENT_OVERRIDES_CSS_STYLESHEET, @@ -22,7 +23,6 @@ import { SELECTOR_TIMELINE_IS_VISIBLE_CSS_CLASS_NAME, TIMELINE_EUI_THEME_ZINDEX_LEVEL, } from '../../../timelines/components/timeline/styles'; -import { useIsMounted } from '../../hooks/use_is_mounted'; const OverlayRootContainer = styled.div` border: none; @@ -246,7 +246,7 @@ export const PageOverlay = memo( // Capture the URL `pathname` that the overlay was opened for useEffect(() => { - if (isMounted) { + if (isMounted()) { setOpenedOnPathName((prevState) => { if (isHidden) { return null; @@ -270,7 +270,7 @@ export const PageOverlay = memo( // If `hideOnUrlPathNameChange` is true, then determine if the pathname changed and if so, call `onHide()` useEffect(() => { if ( - isMounted && + isMounted() && onHide && hideOnUrlPathnameChange && !isHidden && @@ -283,7 +283,7 @@ export const PageOverlay = memo( // Handle adding class names to the `document.body` DOM element useEffect(() => { - if (isMounted) { + if (isMounted()) { if (isHidden) { unSetDocumentBodyOverlayIsVisible(); unSetDocumentBodyLock(); diff --git a/x-pack/plugins/security_solution/public/management/hooks/use_is_mounted.ts b/x-pack/plugins/security_solution/public/management/hooks/use_is_mounted.ts deleted file mode 100644 index 0c5a79b2ca2f..000000000000 --- a/x-pack/plugins/security_solution/public/management/hooks/use_is_mounted.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEffect, useState } from 'react'; - -/** - * Track when a component is mounted/unmounted. Good for use in async processing that may update - * a component's internal state. - */ -export const useIsMounted = (): boolean => { - const [isMounted, setIsMounted] = useState(false); - - useEffect(() => { - setIsMounted(true); - - return () => { - setIsMounted(false); - }; - }, []); - - return isMounted; -}; diff --git a/x-pack/plugins/security_solution/public/management/components/mocks.tsx b/x-pack/plugins/security_solution/public/management/mocks/utils.ts similarity index 93% rename from x-pack/plugins/security_solution/public/management/components/mocks.tsx rename to x-pack/plugins/security_solution/public/management/mocks/utils.ts index 45c12df818fd..946d2d50b05d 100644 --- a/x-pack/plugins/security_solution/public/management/components/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/mocks/utils.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -interface DeferredInterface { + +export interface DeferredInterface { promise: Promise; resolve: (data: T) => void; reject: (e: Error) => void; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx index 37f04ff804c1..b8f7a8c19fbc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx @@ -20,7 +20,7 @@ import { PolicyArtifactsDeleteModal } from './policy_artifacts_delete_modal'; import { exceptionsListAllHttpMocks } from '../../../../../mocks/exceptions_list_http_mocks'; import { ExceptionsListApiClient } from '../../../../../services/exceptions_list/exceptions_list_api_client'; import { POLICY_ARTIFACT_DELETE_MODAL_LABELS } from './translations'; -import { getDeferred } from '../../../../../components/mocks'; +import { getDeferred } from '../../../../../mocks/utils'; const listType: Array = [ 'endpoint_events', diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index b2984037b030..56922e26edc2 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25702,9 +25702,6 @@ "xpack.securitySolution.endpoint.resolver.relatedEventLimitTitle": "Cette liste inclut {numberOfEntries} événements de processus.", "xpack.securitySolution.endpointPolicyStatus.revisionNumber": "rév. {revNumber}", "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "{ errorCount, plural, =1 {Erreur rencontrée} other {Erreurs rencontrées}} :", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessage": "L'erreur suivante a été rencontrée : {error}", - "xpack.securitySolution.endpointResponseActions.killProcess.performApiErrorMessage": "L'erreur suivante a été rencontrée : {error}", - "xpack.securitySolution.endpointResponseActions.suspendProcess.performApiErrorMessage": "L'erreur suivante a été rencontrée : {error}", "xpack.securitySolution.event.reason.reasonRendererTitle": "Outil de rendu d'événement : {eventRendererName} ", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "Le champ {field} est un objet, et il est composé de champs imbriqués qui peuvent être ajoutés en tant que colonne", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "Afficher la colonne {field}", @@ -28124,8 +28121,6 @@ "xpack.securitySolution.endpointManagement.noPermissionsSubText": "Vous devez disposer du rôle de superutilisateur pour utiliser cette fonctionnalité. Si vous ne disposez pas de ce rôle, ni d'autorisations pour modifier les rôles d'utilisateur, contactez votre administrateur Kibana.", "xpack.securitySolution.endpointManagemnet.noPermissionsText": "Vous ne disposez pas des autorisations Kibana requises pour utiliser Elastic Security Administration", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "Politique appliquée", - "xpack.securitySolution.endpointResponseActions.getProcesses.errorMessageTitle": "Échec de l’obtention des processus", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessageTitle": "Échec de l’exécution de l’action d’obtention des processus", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.command": "COMMANDE", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.enityId": "ID D’ENTITÉ", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.pid": "PID", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a09bdfe578cb..1441559ad00a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25677,9 +25677,6 @@ "xpack.securitySolution.endpoint.resolver.relatedEventLimitTitle": "このリストには、{numberOfEntries} 件のプロセスイベントが含まれています。", "xpack.securitySolution.endpointPolicyStatus.revisionNumber": "rev. {revNumber}", "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "次の{ errorCount, plural, other {件のエラー}}が発生しました:", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessage": "次のエラーが発生しました:{error}", - "xpack.securitySolution.endpointResponseActions.killProcess.performApiErrorMessage": "次のエラーが発生しました:{error}", - "xpack.securitySolution.endpointResponseActions.suspendProcess.performApiErrorMessage": "次のエラーが発生しました:{error}", "xpack.securitySolution.event.reason.reasonRendererTitle": "イベントレンダラー:{eventRendererName} ", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "{field}フィールドはオブジェクトであり、列として追加できるネストされたフィールドに分解されます", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "{field} 列を表示", @@ -28099,8 +28096,6 @@ "xpack.securitySolution.endpointManagement.noPermissionsSubText": "この機能を使用するには、スーパーユーザーロールが必要です。スーパーユーザーロールがなく、ユーザーロールを編集する権限もない場合は、Kibana管理者に問い合わせてください。", "xpack.securitySolution.endpointManagemnet.noPermissionsText": "Elastic Security Administrationを使用するために必要なKibana権限がありません。", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "ポリシーが適用されました", - "xpack.securitySolution.endpointResponseActions.getProcesses.errorMessageTitle": "プロセスの取得アクションが失敗しました", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessageTitle": "プロセスの取得アクションの実行が失敗しました", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.command": "コマンド", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.enityId": "エンティティID", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.pid": "PID", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5ade45168ae7..d1651dddce02 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25711,9 +25711,6 @@ "xpack.securitySolution.endpoint.resolver.relatedEventLimitTitle": "此列表包括 {numberOfEntries} 个进程事件。", "xpack.securitySolution.endpointPolicyStatus.revisionNumber": "修订版 {revNumber}", "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "遇到以下{ errorCount, plural, other {错误}}:", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessage": "遇到以下错误:{error}", - "xpack.securitySolution.endpointResponseActions.killProcess.performApiErrorMessage": "遇到以下错误:{error}", - "xpack.securitySolution.endpointResponseActions.suspendProcess.performApiErrorMessage": "遇到以下错误:{error}", "xpack.securitySolution.event.reason.reasonRendererTitle": "事件渲染器:{eventRendererName} ", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "{field} 字段是对象,并分解为可以添加为列的嵌套字段", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "查看 {field} 列", @@ -28133,8 +28130,6 @@ "xpack.securitySolution.endpointManagement.noPermissionsSubText": "您必须具有超级用户角色才能使用此功能。如果您不具有超级用户角色,且无权编辑用户角色,请与 Kibana 管理员联系。", "xpack.securitySolution.endpointManagemnet.noPermissionsText": "您没有所需的 Kibana 权限,无法使用 Elastic Security 管理", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "已应用策略", - "xpack.securitySolution.endpointResponseActions.getProcesses.errorMessageTitle": "获取进程操作失败", - "xpack.securitySolution.endpointResponseActions.getProcesses.performApiErrorMessageTitle": "执行获取进程操作失败", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.command": "命令", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.enityId": "实体 ID", "xpack.securitySolution.endpointResponseActions.getProcesses.table.header.pid": "PID", From f571f8082716ecda912a2d380118138cf28d4663 Mon Sep 17 00:00:00 2001 From: Andrew Macri Date: Tue, 27 Sep 2022 14:15:53 -0600 Subject: [PATCH 039/185] [Security Solution] Disable renderer hover actions in the Rules preview flyout (#141546) ## [Security Solution] Disable hover actions on renderers in the Rules preview flyout This PR addresses issue by disabling hover actions on renderers in the _Rules preview_ flyout, per the Before and After screenshots below: ### Before ![before](https://user-images.githubusercontent.com/4459398/191833235-ed9974d8-3d31-4da1-8db6-e5c500003f6b.png) _Above: Before the fix, actions were displayed when hovering over a rendered field in the Rules preview flyout_ ### After ![after](https://user-images.githubusercontent.com/4459398/191833522-427649ab-9670-48df-8a87-1980fe8e4070.png) _Above: After the fix, actions are NOT displayed when hovering over a rendered field in the Rules preview flyout_ --- .../drag_and_drop/draggable_wrapper.test.tsx | 105 +++++++++++++++++- .../drag_and_drop/draggable_wrapper.tsx | 6 +- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx index c12643a30f94..b6a3995534d1 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx @@ -5,16 +5,24 @@ * 2.0. */ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { shallow } from 'enzyme'; import React from 'react'; import type { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd'; -import { waitFor } from '@testing-library/react'; + import '../../mock/match_media'; +import { TimelineId } from '../../../../common/types'; import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers'; +import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; -import { ConditionalPortal, DraggableWrapper, getStyle } from './draggable_wrapper'; +import { + ConditionalPortal, + disableHoverActions, + DraggableWrapper, + getStyle, +} from './draggable_wrapper'; import { useMountAppended } from '../../utils/use_mount_appended'; jest.mock('../../lib/kibana'); @@ -27,6 +35,26 @@ jest.mock('@elastic/eui', () => { }; }); +const timelineIdsWithHoverActions = [ + undefined, + TimelineId.active, + TimelineId.alternateTest, + TimelineId.casePage, + TimelineId.detectionsPage, + TimelineId.detectionsRulesDetailsPage, + TimelineId.hostsPageEvents, + TimelineId.hostsPageSessions, + TimelineId.kubernetesPageSessions, + TimelineId.networkPageEvents, + TimelineId.test, + TimelineId.usersPageEvents, +]; + +const timelineIdsNoHoverActions = [ + TimelineId.rulePreview, + ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID, +]; + describe('DraggableWrapper', () => { const dataProvider = mockDataProviders[0]; const message = 'draggable wrapper content'; @@ -36,6 +64,15 @@ describe('DraggableWrapper', () => { jest.useFakeTimers(); }); + afterEach(() => { + const portal = document.querySelector('[data-euiportal="true"]'); + if (portal != null) { + portal.innerHTML = ''; + } + + jest.useRealTimers(); + }); + describe('rendering', () => { test('it renders against the snapshot', () => { const wrapper = shallow( @@ -103,6 +140,56 @@ describe('DraggableWrapper', () => { expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(true); }); }); + + timelineIdsWithHoverActions.forEach((timelineId) => { + test(`it renders hover actions (by default) when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => { + const isDraggable = false; + + const { container } = render( + + + message} + timelineId={timelineId} + /> + + + ); + + fireEvent.mouseEnter(container.querySelector('[data-test-subj="withHoverActionsButton"]')!); + + await waitFor(() => { + expect(screen.getByTestId('hover-actions-copy-button')).toBeInTheDocument(); + }); + }); + }); + + timelineIdsNoHoverActions.forEach((timelineId) => { + test(`it does NOT render hover actions when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => { + const isDraggable = false; + + const { container } = render( + + + message} + timelineId={timelineId} + /> + + + ); + + fireEvent.mouseEnter(container.querySelector('[data-test-subj="withHoverActionsButton"]')!); + + await waitFor(() => { + expect(screen.queryByTestId('hover-actions-copy-button')).not.toBeInTheDocument(); + }); + }); + }); }); describe('text truncation styling', () => { @@ -192,4 +279,18 @@ describe('ConditionalPortal', () => { expect(getStyle(style, snapshot)).toHaveProperty('transitionDuration', '0.00000001s'); }); }); + + describe('disableHoverActions', () => { + timelineIdsNoHoverActions.forEach((timelineId) => + test(`it returns true when timelineId is ${timelineId}`, () => { + expect(disableHoverActions(timelineId)).toBe(true); + }) + ); + + timelineIdsWithHoverActions.forEach((timelineId) => + test(`it returns false when timelineId is ${timelineId}`, () => { + expect(disableHoverActions(timelineId)).toBe(false); + }) + ); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx index f972bcf463b5..887f1635c08c 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx @@ -18,6 +18,7 @@ import { Draggable, Droppable } from 'react-beautiful-dnd'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; +import { TimelineId } from '../../../../common/types'; import { dragAndDropActions } from '../../store/drag_and_drop'; import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants'; @@ -108,6 +109,9 @@ interface Props { onFilterAdded?: () => void; } +export const disableHoverActions = (timelineId: string | undefined): boolean => + [TimelineId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? ''); + /** * Wraps a draggable component to handle registration / unregistration of the * data provider associated with the item being dropped @@ -370,7 +374,7 @@ const DraggableWrapperComponent: React.FC = ({ From 27483d5aa90d80d2896d92b22da148372c09195e Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Tue, 27 Sep 2022 17:18:41 -0400 Subject: [PATCH 040/185] Fix curl documentation (#141971) --- docs/user/reporting/script-example.asciidoc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/user/reporting/script-example.asciidoc b/docs/user/reporting/script-example.asciidoc index 1d8e824798e7..937e140bd67a 100644 --- a/docs/user/reporting/script-example.asciidoc +++ b/docs/user/reporting/script-example.asciidoc @@ -3,7 +3,7 @@ URL that you use to download the report. Use the `GET` method in the HTTP reques To queue CSV report generation using the `POST` URL with cURL: -["source","sh",subs="attributes"] +[source,curl] --------------------------------------------------------- curl \ -XPOST \ <1> @@ -11,7 +11,6 @@ curl \ -H 'kbn-xsrf: true' \ <3> 'http://0.0.0.0:5601/api/reporting/generate/csv?jobParams=...' <4> --------------------------------------------------------- -// CONSOLE <1> The required `POST` method. <2> The user credentials for a user with permission to access {kib} and {report-features}. @@ -20,7 +19,7 @@ curl \ An example response for a successfully queued report: -[source,json] +[source,js] --------------------------------------------------------- { "path": "/api/reporting/jobs/download/jxzaofkc0ykpf4062305t068", <1> @@ -35,7 +34,6 @@ An example response for a successfully queued report: } } --------------------------------------------------------- -// CONSOLE <1> The relative path on the {kib} host for downloading the report. <2> (Not included in the example) Internal representation of the reporting job, as found in the `.reporting-*` index. From e4080d5b64fa563382cf8a8f9c59f3bb9808d65e Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Tue, 27 Sep 2022 17:34:49 -0500 Subject: [PATCH 041/185] [Security Solution] use endpoint rbac for isolate/unisolate host (#141810) --- x-pack/plugins/fleet/common/authz.test.ts | 5 +- .../plugins/fleet/common/constants/authz.ts | 2 +- x-pack/plugins/fleet/common/mocks.ts | 15 ++++++ .../endpoint/service/authz/authz.test.ts | 34 ++++++++++++ .../common/endpoint/service/authz/authz.ts | 8 ++- .../endpoint/use_endpoint_privileges.test.ts | 11 ++-- .../details/components/actions_menu.test.tsx | 52 ++++++++++++++----- .../view/hooks/use_endpoint_action_items.tsx | 14 ++--- .../pages/endpoint_hosts/view/index.test.tsx | 32 ++++++++---- 9 files changed, 134 insertions(+), 39 deletions(-) diff --git a/x-pack/plugins/fleet/common/authz.test.ts b/x-pack/plugins/fleet/common/authz.test.ts index ced783c6d4ad..cadb90651b01 100644 --- a/x-pack/plugins/fleet/common/authz.test.ts +++ b/x-pack/plugins/fleet/common/authz.test.ts @@ -15,7 +15,10 @@ import { ENDPOINT_PRIVILEGES } from './constants'; const SECURITY_SOLUTION_ID = DEFAULT_APP_CATEGORIES.security.id; -function generateActions(privileges: string[] = [], overrides: Record = {}) { +function generateActions( + privileges: typeof ENDPOINT_PRIVILEGES, + overrides: Record = {} +) { return privileges.reduce((acc, privilege) => { const executePackageAction = overrides[privilege] || false; diff --git a/x-pack/plugins/fleet/common/constants/authz.ts b/x-pack/plugins/fleet/common/constants/authz.ts index 3bf1aeb46adc..d7a0ca4ade2e 100644 --- a/x-pack/plugins/fleet/common/constants/authz.ts +++ b/x-pack/plugins/fleet/common/constants/authz.ts @@ -23,4 +23,4 @@ export const ENDPOINT_PRIVILEGES = [ 'writeHostIsolation', 'writeProcessOperations', 'writeFileOperations', -]; +] as const; diff --git a/x-pack/plugins/fleet/common/mocks.ts b/x-pack/plugins/fleet/common/mocks.ts index bc8880ed385a..d5aca8398abd 100644 --- a/x-pack/plugins/fleet/common/mocks.ts +++ b/x-pack/plugins/fleet/common/mocks.ts @@ -7,6 +7,7 @@ import type { DeletePackagePoliciesResponse, NewPackagePolicy, PackagePolicy } from './types'; import type { FleetAuthz } from './authz'; +import { ENDPOINT_PRIVILEGES } from './constants'; export const createNewPackagePolicyMock = (): NewPackagePolicy => { return { @@ -61,6 +62,15 @@ export const deletePackagePolicyMock = (): DeletePackagePoliciesResponse => { * Creates mock `authz` object */ export const createFleetAuthzMock = (): FleetAuthz => { + const endpointActions = ENDPOINT_PRIVILEGES.reduce((acc, privilege) => { + return { + ...acc, + [privilege]: { + executePackageAction: true, + }, + }; + }, {}); + return { fleet: { all: true, @@ -80,5 +90,10 @@ export const createFleetAuthzMock = (): FleetAuthz => { readIntegrationPolicies: true, writeIntegrationPolicies: true, }, + packagePrivileges: { + endpoint: { + actions: endpointActions, + }, + }, }; }; diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts index 3434e95f29b4..b9c0dcff2054 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts @@ -107,6 +107,40 @@ describe('Endpoint Authz service', () => { ); }); }); + + describe('endpoint rbac is enabled', () => { + describe('canIsolateHost', () => { + it('should be true if packagePrivilege.writeHostIsolation is true', () => { + fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = + true; + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz.canIsolateHost).toBe(true); + }); + + it('should be false if packagePrivilege.writeHostIsolation is false', () => { + fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = + false; + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz.canIsolateHost).toBe(false); + }); + }); + + describe('canUnIsolateHost', () => { + it('should be true if packagePrivilege.writeHostIsolation is true', () => { + fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = + true; + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz.canUnIsolateHost).toBe(true); + }); + + it('should be false if packagePrivilege.writeHostIsolation is false', () => { + fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = + false; + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz.canUnIsolateHost).toBe(false); + }); + }); + }); }); describe('getEndpointAuthzInitialState()', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts index 6f578ec24d85..bc40bd11f5d7 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts @@ -28,14 +28,18 @@ export const calculateEndpointAuthz = ( const isPlatinumPlusLicense = licenseService.isPlatinumPlus(); const isEnterpriseLicense = licenseService.isEnterprise(); const hasEndpointManagementAccess = userRoles.includes('superuser'); + const canIsolateHost = isEndpointRbacEnabled + ? fleetAuthz.packagePrivileges?.endpoint?.actions?.writeHostIsolation?.executePackageAction || + false + : hasEndpointManagementAccess; return { canAccessFleet: fleetAuthz?.fleet.all ?? userRoles.includes('superuser'), canAccessEndpointManagement: hasEndpointManagementAccess, canCreateArtifactsByPolicy: hasEndpointManagementAccess && isPlatinumPlusLicense, // Response Actions - canIsolateHost: isPlatinumPlusLicense && hasEndpointManagementAccess, - canUnIsolateHost: hasEndpointManagementAccess, + canIsolateHost: isPlatinumPlusLicense && canIsolateHost, + canUnIsolateHost: canIsolateHost, canKillProcess: hasEndpointManagementAccess && isEnterpriseLicense, canSuspendProcess: hasEndpointManagementAccess && isEnterpriseLicense, canGetRunningProcesses: hasEndpointManagementAccess && isEnterpriseLicense, diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts index 29da5688357b..a0d23820025b 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts @@ -7,15 +7,19 @@ import type { RenderHookResult, RenderResult } from '@testing-library/react-hooks'; import { act, renderHook } from '@testing-library/react-hooks'; -import { useCurrentUser, useKibana } from '../../../lib/kibana'; -import { useEndpointPrivileges } from './use_endpoint_privileges'; + import { securityMock } from '@kbn/security-plugin/public/mocks'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { createFleetAuthzMock } from '@kbn/fleet-plugin/common'; + +import type { EndpointPrivileges } from '../../../../../common/endpoint/types'; +import { useCurrentUser, useKibana } from '../../../lib/kibana'; import { licenseService } from '../../../hooks/use_license'; +import { useEndpointPrivileges } from './use_endpoint_privileges'; import { getEndpointPrivilegesInitialStateMock } from './mocks'; -import type { EndpointPrivileges } from '../../../../../common/endpoint/types'; import { getEndpointPrivilegesInitialState } from './utils'; +const useKibanaMock = useKibana as jest.Mocked; jest.mock('../../../lib/kibana'); jest.mock('../../../hooks/use_license', () => { const licenseServiceInstance = { @@ -47,6 +51,7 @@ describe('When using useEndpointPrivileges hook', () => { }); (useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser); + useKibanaMock().services.fleet!.authz = createFleetAuthzMock(); licenseServiceMock.isPlatinumPlus.mockReturnValue(true); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx index 6fc8a99ee732..35fe96d94d91 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx @@ -14,6 +14,9 @@ import { act } from '@testing-library/react'; import { endpointPageHttpMock } from '../../../mocks'; import { fireEvent } from '@testing-library/dom'; import { licenseService } from '../../../../../../common/hooks/use_license'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; +import { initialUserPrivilegesState } from '../../../../../../common/components/user_privileges/user_privileges_context'; +import { getUserPrivilegesMockDefaultValue } from '../../../../../../common/components/user_privileges/__mocks__'; jest.mock('../../../../../../common/lib/kibana/kibana_react', () => { const originalModule = jest.requireActual('../../../../../../common/lib/kibana/kibana_react'); @@ -31,6 +34,7 @@ jest.mock('../../../../../../common/lib/kibana/kibana_react', () => { }; }); jest.mock('../../../../../../common/hooks/use_license'); +jest.mock('../../../../../../common/components/user_privileges'); describe('When using the Endpoint Details Actions Menu', () => { let render: () => Promise>; @@ -59,6 +63,8 @@ describe('When using the Endpoint Details Actions Menu', () => { waitForAction = mockedContext.middlewareSpy.waitForAction; httpMocks = endpointPageHttpMock(mockedContext.coreStart.http); + (useUserPrivileges as jest.Mock).mockReturnValue(getUserPrivilegesMockDefaultValue()); + act(() => { mockedContext.history.push( '/administration/endpoints?selected_endpoint=5fe11314-678c-413e-87a2-b4a3461878ee' @@ -80,6 +86,10 @@ describe('When using the Endpoint Details Actions Menu', () => { }; }); + afterEach(() => { + (useUserPrivileges as jest.Mock).mockClear(); + }); + it('should not show the response actions history link', async () => { await render(); expect(renderResult.queryByTestId('actionsLink')).toBeNull(); @@ -121,18 +131,38 @@ describe('When using the Endpoint Details Actions Menu', () => { describe('and endpoint host is isolated', () => { beforeEach(() => setEndpointMetadataResponse(true)); - it('should display Unisolate action', async () => { - await render(); - expect(renderResult.getByTestId('unIsolateLink')).not.toBeNull(); + describe('and user has unisolate privilege', () => { + it('should display Unisolate action', async () => { + await render(); + expect(renderResult.getByTestId('unIsolateLink')).not.toBeNull(); + }); + + it('should navigate via router when unisolate is clicked', async () => { + await render(); + act(() => { + fireEvent.click(renderResult.getByTestId('unIsolateLink')); + }); + + expect(coreStart.application.navigateToApp).toHaveBeenCalled(); + }); }); - it('should navigate via router when unisolate is clicked', async () => { - await render(); - act(() => { - fireEvent.click(renderResult.getByTestId('unIsolateLink')); + describe('and user does not have unisolate privilege', () => { + beforeEach(() => { + (useUserPrivileges as jest.Mock).mockReturnValue({ + ...initialUserPrivilegesState(), + endpointPrivileges: { + ...initialUserPrivilegesState().endpointPrivileges, + canIsolateHost: false, + canUnIsolateHost: false, + }, + }); }); - expect(coreStart.application.navigateToApp).toHaveBeenCalled(); + it('should not display unisolate action', async () => { + await render(); + expect(renderResult.queryByTestId('unIsolateLink')).toBeNull(); + }); }); }); @@ -143,12 +173,6 @@ describe('When using the Endpoint Details Actions Menu', () => { afterEach(() => licenseServiceMock.isPlatinumPlus.mockReturnValue(true)); - it('should not show the `isolate` action', async () => { - setEndpointMetadataResponse(); - await render(); - expect(renderResult.queryByTestId('isolateLink')).toBeNull(); - }); - it('should still show `unisolate` action for endpoints that are currently isolated', async () => { setEndpointMetadataResponse(true); await render(); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 40fd81c4ab58..9a9c884a3979 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -19,7 +19,6 @@ import { agentPolicies, uiQueryParams } from '../../store/selectors'; import { useAppUrl } from '../../../../../common/lib/kibana/hooks'; import type { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; import { isEndpointHostIsolated } from '../../../../../common/utils/validators'; -import { useLicense } from '../../../../../common/hooks/use_license'; import { isIsolationSupported } from '../../../../../../common/endpoint/service/host_isolation/utils'; import { useDoesEndpointSupportResponder } from '../../../../../common/hooks/endpoint/use_does_endpoint_support_responder'; import { UPGRADE_ENDPOINT_FOR_RESPONDER } from '../../../../../common/translations'; @@ -36,7 +35,6 @@ export const useEndpointActionItems = ( endpointMetadata: MaybeImmutable | undefined, options?: Options ): ContextMenuItemNavByRouterProps[] => { - const isPlatinumPlus = useLicense().isPlatinumPlus(); const { getAppUrl } = useAppUrl(); const fleetAgentPolicies = useEndpointSelector(agentPolicies); const allCurrentUrlParams = useEndpointSelector(uiQueryParams); @@ -44,7 +42,8 @@ export const useEndpointActionItems = ( const isResponseActionsConsoleEnabled = useIsExperimentalFeatureEnabled( 'responseActionsConsoleEnabled' ); - const canAccessResponseConsole = useUserPrivileges().endpointPrivileges.canAccessResponseConsole; + const { canAccessResponseConsole, canIsolateHost, canUnIsolateHost } = + useUserPrivileges().endpointPrivileges; const isResponderCapabilitiesEnabled = useDoesEndpointSupportResponder(endpointMetadata); return useMemo(() => { @@ -82,8 +81,8 @@ export const useEndpointActionItems = ( const isolationActions = []; - if (isIsolated) { - // Un-isolate is always available to users regardless of license level + if (isIsolated && canUnIsolateHost) { + // Un-isolate is available to users regardless of license level if they have unisolate permissions isolationActions.push({ 'data-test-subj': 'unIsolateLink', icon: 'lockOpen', @@ -100,7 +99,7 @@ export const useEndpointActionItems = ( /> ), }); - } else if (isPlatinumPlus && isolationSupported) { + } else if (isolationSupported && canIsolateHost) { // For Platinum++ licenses, users also have ability to isolate isolationActions.push({ 'data-test-subj': 'isolateLink', @@ -260,10 +259,11 @@ export const useEndpointActionItems = ( endpointMetadata, fleetAgentPolicies, getAppUrl, - isPlatinumPlus, isResponseActionsConsoleEnabled, showEndpointResponseActionsConsole, options?.isEndpointList, isResponderCapabilitiesEnabled, + canIsolateHost, + canUnIsolateHost, ]); }; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index b85ad2cc7f6a..7d8d625d97e3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -1007,6 +1007,7 @@ describe('when on the endpoint list page', () => { let agentId: string; let agentPolicyId: string; let renderResult: ReturnType; + let endpointActionsButton: HTMLElement; // 2nd endpoint only has isolation capabilities const mockEndpointListApi = () => { @@ -1081,13 +1082,7 @@ describe('when on the endpoint list page', () => { beforeEach(async () => { mockEndpointListApi(); - (useUserPrivileges as jest.Mock).mockReturnValue({ - ...mockInitialUserPrivilegesState(), - endpointPrivileges: { - ...mockInitialUserPrivilegesState().endpointPrivileges, - canAccessResponseConsole: true, - }, - }); + (useUserPrivileges as jest.Mock).mockReturnValue(getUserPrivilegesMockDefaultValue()); reactTestingLibrary.act(() => { history.push(`${MANAGEMENT_PATH}/endpoints`); @@ -1097,9 +1092,7 @@ describe('when on the endpoint list page', () => { await middlewareSpy.waitForAction('serverReturnedEndpointList'); await middlewareSpy.waitForAction('serverReturnedEndpointAgentPolicies'); - const endpointActionsButton = ( - await renderResult.findAllByTestId('endpointTableRowActions') - )[0]; + endpointActionsButton = (await renderResult.findAllByTestId('endpointTableRowActions'))[0]; reactTestingLibrary.act(() => { reactTestingLibrary.fireEvent.click(endpointActionsButton); @@ -1108,7 +1101,6 @@ describe('when on the endpoint list page', () => { afterEach(() => { jest.clearAllMocks(); - (useUserPrivileges as jest.Mock).mockReturnValue(getUserPrivilegesMockDefaultValue()); }); it('shows the Responder option when all 3 processes capabilities are present in the endpoint', async () => { @@ -1141,6 +1133,24 @@ describe('when on the endpoint list page', () => { ); }); + it('hides isolate host option if canIsolateHost is false', () => { + (useUserPrivileges as jest.Mock).mockReturnValue({ + ...mockInitialUserPrivilegesState(), + endpointPrivileges: { + ...mockInitialUserPrivilegesState().endpointPrivileges, + canIsolateHost: false, + }, + }); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(endpointActionsButton); + }); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(endpointActionsButton); + }); + const isolateLink = screen.queryByTestId('isolateLink'); + expect(isolateLink).toBeNull(); + }); + it('navigates to the Security Solution Host Details page', async () => { const hostLink = await renderResult.findByTestId('hostLink'); expect(hostLink.getAttribute('href')).toEqual( From cc9f1c640962cc55cbd9181fd4ecd1d9499a0fa7 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 27 Sep 2022 17:53:37 -0600 Subject: [PATCH 042/185] [Maps] fix Go To - lat/long values outside expected range cause blank Maps app (#141873) * [Maps] fix Go To - lat/long values outside expected range cause blank Maps app * UTM form * wire UTM form * add unit tests * fix test names * fix expects * fix functional tests * review feedback --- .../set_view_control/decimal_degrees_form.tsx | 149 ++++ .../set_view_control/mgrs_form.tsx | 149 ++++ .../set_view_control/number_form_row.tsx | 6 + .../set_view_control/set_view_control.tsx | 733 +----------------- .../set_view_control/set_view_form.tsx | 134 ++++ .../set_view_control/utils.test.ts | 52 ++ .../toolbar_overlay/set_view_control/utils.ts | 91 +++ .../set_view_control/utm_form.tsx | 209 +++++ 8 files changed, 804 insertions(+), 719 deletions(-) create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/decimal_degrees_form.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/mgrs_form.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/number_form_row.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_form.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.test.ts create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.ts create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utm_form.tsx diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/decimal_degrees_form.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/decimal_degrees_form.tsx new file mode 100644 index 000000000000..03b23e409b77 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/decimal_degrees_form.tsx @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { ChangeEvent, Component } from 'react'; +import { + EuiForm, + EuiFormRow, + EuiButton, + EuiFieldNumber, + EuiTextAlign, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MapCenter, MapSettings } from '../../../../common/descriptor_types'; +import { withinRange } from './utils'; + +interface Props { + settings: MapSettings; + zoom: number; + center: MapCenter; + onSubmit: (lat: number, lon: number, zoom: number) => void; +} + +interface State { + lat: number | string; + lon: number | string; + zoom: number | string; +} + +export class DecimalDegreesForm extends Component { + state: State = { + lat: this.props.center.lat, + lon: this.props.center.lon, + zoom: this.props.zoom, + }; + + _onLatChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + lat: isNaN(sanitizedValue) ? '' : sanitizedValue, + }); + }; + + _onLonChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + lon: isNaN(sanitizedValue) ? '' : sanitizedValue, + }); + }; + + _onZoomChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + zoom: isNaN(sanitizedValue) ? '' : sanitizedValue, + }); + }; + + _onSubmit = () => { + const { lat, lon, zoom } = this.state; + this.props.onSubmit(lat as number, lon as number, zoom as number); + }; + + render() { + const { isInvalid: isLatInvalid, error: latError } = withinRange(this.state.lat, -90, 90); + const { isInvalid: isLonInvalid, error: lonError } = withinRange(this.state.lon, -180, 180); + const { isInvalid: isZoomInvalid, error: zoomError } = withinRange( + this.state.zoom, + this.props.settings.minZoom, + this.props.settings.maxZoom + ); + + return ( + + + + + + + + + + + + + + + + + + + + + + ); + } +} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/mgrs_form.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/mgrs_form.tsx new file mode 100644 index 000000000000..48455f89b746 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/mgrs_form.tsx @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 _ from 'lodash'; +import React, { ChangeEvent, Component } from 'react'; +import { + EuiForm, + EuiFormRow, + EuiButton, + EuiFieldNumber, + EuiFieldText, + EuiTextAlign, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MapCenter, MapSettings } from '../../../../common/descriptor_types'; +import { ddToMGRS, mgrsToDD, withinRange } from './utils'; + +interface Props { + settings: MapSettings; + zoom: number; + center: MapCenter; + onSubmit: (lat: number, lon: number, zoom: number) => void; +} + +interface State { + mgrs: string; + zoom: number | string; +} + +export class MgrsForm extends Component { + state: State = { + mgrs: ddToMGRS(this.props.center.lat, this.props.center.lon), + zoom: this.props.zoom, + }; + + _toPoint() { + return this.state.mgrs === '' ? undefined : mgrsToDD(this.state.mgrs); + } + + _isMgrsInvalid() { + const point = this._toPoint(); + return ( + point === undefined || + !point.north || + _.isNaN(point.north) || + !point.south || + _.isNaN(point.south) || + !point.east || + _.isNaN(point.east) || + !point.west || + _.isNaN(point.west) + ); + } + + _onMGRSChange = (evt: ChangeEvent) => { + this.setState({ + mgrs: _.isNull(evt.target.value) ? '' : evt.target.value, + }); + }; + + _onZoomChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + zoom: isNaN(sanitizedValue) ? '' : sanitizedValue, + }); + }; + + _onSubmit = () => { + const point = this._toPoint(); + if (point) { + this.props.onSubmit(point.north, point.east, this.state.zoom as number); + } + }; + + render() { + const isMgrsInvalid = this._isMgrsInvalid(); + const mgrsError = isMgrsInvalid + ? i18n.translate('xpack.maps.setViewControl.mgrsInvalid', { + defaultMessage: 'MGRS is invalid', + }) + : null; + const { isInvalid: isZoomInvalid, error: zoomError } = withinRange( + this.state.zoom, + this.props.settings.minZoom, + this.props.settings.maxZoom + ); + + return ( + + + + + + + + + + + + + + + + + + ); + } +} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/number_form_row.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/number_form_row.tsx new file mode 100644 index 000000000000..1fec1c76430e --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/number_form_row.tsx @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx index eb0b1cfc1dda..906cbb8a0bb0 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.tsx @@ -5,50 +5,11 @@ * 2.0. */ -import React, { ChangeEvent, Component, Fragment } from 'react'; -import { - EuiForm, - EuiFormRow, - EuiButton, - EuiFieldNumber, - EuiFieldText, - EuiButtonIcon, - EuiPopover, - EuiTextAlign, - EuiSpacer, - EuiPanel, -} from '@elastic/eui'; -import { EuiButtonEmpty } from '@elastic/eui'; -import { EuiRadioGroup } from '@elastic/eui'; +import React, { Component } from 'react'; +import { EuiButtonIcon, EuiPopover, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import * as usng from 'usng.js'; -import { isNaN, isNull } from 'lodash'; import { MapCenter, MapSettings } from '../../../../common/descriptor_types'; - -export const COORDINATE_SYSTEM_DEGREES_DECIMAL = 'dd'; -export const COORDINATE_SYSTEM_MGRS = 'mgrs'; -export const COORDINATE_SYSTEM_UTM = 'utm'; - -export const DEFAULT_SET_VIEW_COORDINATE_SYSTEM = COORDINATE_SYSTEM_DEGREES_DECIMAL; - -// @ts-ignore -const converter = new usng.Converter(); - -const COORDINATE_SYSTEMS = [ - { - id: COORDINATE_SYSTEM_DEGREES_DECIMAL, - label: 'Degrees Decimal', - }, - { - id: COORDINATE_SYSTEM_UTM, - label: 'UTM', - }, - { - id: COORDINATE_SYSTEM_MGRS, - label: 'MGRS', - }, -]; +import { SetViewForm } from './set_view_form'; export interface Props { settings: MapSettings; @@ -59,73 +20,17 @@ export interface Props { interface State { isPopoverOpen: boolean; - lat: number | string; - lon: number | string; - zoom: number | string; - coord: string; - mgrs: string; - utm: { - northing: string; - easting: string; - zoneNumber: string; - zoneLetter: string | undefined; - zone: string; - }; - isCoordPopoverOpen: boolean; - prevView: string | undefined; } export class SetViewControl extends Component { state: State = { isPopoverOpen: false, - lat: 0, - lon: 0, - zoom: 0, - coord: DEFAULT_SET_VIEW_COORDINATE_SYSTEM, - mgrs: '', - utm: { - northing: '', - easting: '', - zoneNumber: '', - zoneLetter: '', - zone: '', - }, - isCoordPopoverOpen: false, - prevView: '', }; - static getDerivedStateFromProps(nextProps: Props, prevState: State) { - const nextView = getViewString(nextProps.center.lat, nextProps.center.lon, nextProps.zoom); - - const utm = convertLatLonToUTM(nextProps.center.lat, nextProps.center.lon); - const mgrs = convertLatLonToMGRS(nextProps.center.lat, nextProps.center.lon); - - if (nextView !== prevState.prevView) { - return { - lat: nextProps.center.lat, - lon: nextProps.center.lon, - zoom: nextProps.zoom, - utm, - mgrs, - prevView: nextView, - }; - } - - return null; - } - _togglePopover = () => { - if (this.state.isPopoverOpen) { - this._closePopover(); - return; - } - - this.setState({ - lat: this.props.center.lat, - lon: this.props.center.lon, - zoom: this.props.zoom, - isPopoverOpen: true, - }); + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); }; _closePopover = () => { @@ -134,567 +39,11 @@ export class SetViewControl extends Component { }); }; - _onCoordinateSystemChange = (coordId: string) => { - this.setState({ - coord: coordId, - }); - }; - - _onLatChange = (evt: ChangeEvent) => { - this._onChange('lat', evt); - }; - - _onLonChange = (evt: ChangeEvent) => { - this._onChange('lon', evt); - }; - - _onZoomChange = (evt: ChangeEvent) => { - const sanitizedValue = parseFloat(evt.target.value); - this.setState({ - ['zoom']: isNaN(sanitizedValue) ? '' : sanitizedValue, - }); - }; - - _onUTMZoneChange = (evt: ChangeEvent) => { - this._onUTMChange('zone', evt); - }; - - _onUTMEastingChange = (evt: ChangeEvent) => { - this._onUTMChange('easting', evt); - }; - - _onUTMNorthingChange = (evt: ChangeEvent) => { - this._onUTMChange('northing', evt); - }; - - _onMGRSChange = (evt: ChangeEvent) => { - this.setState( - { - ['mgrs']: isNull(evt.target.value) ? '' : evt.target.value, - }, - this._syncToMGRS - ); - }; - - _onUTMChange = (name: 'easting' | 'northing' | 'zone', evt: ChangeEvent) => { - const value = evt.target.value; - const updateObj = { ...this.state.utm }; - updateObj[name] = isNull(value) ? '' : value; - if (name === 'zone' && value.length > 0) { - const zoneLetter = value.substring(value.length - 1); - const zoneNumber = value.substring(0, value.length - 1); - updateObj.zoneLetter = isNaN(zoneLetter) ? zoneLetter : ''; - updateObj.zoneNumber = isNaN(zoneNumber) ? '' : zoneNumber; - } - this.setState( - { - // @ts-ignore - ['utm']: updateObj, - }, - this._syncToUTM - ); - }; - - _onChange = (name: 'lat' | 'lon', evt: ChangeEvent) => { - const sanitizedValue = parseFloat(evt.target.value); - - this.setState( - // @ts-ignore - { - [name]: isNaN(sanitizedValue) ? '' : sanitizedValue, - }, - this._syncToLatLon - ); - }; - - /** - * Sync all coordinates to the lat/lon that is set - */ - _syncToLatLon = () => { - if (this.state.lat !== '' && this.state.lon !== '') { - const utm = convertLatLonToUTM(this.state.lat, this.state.lon); - const mgrs = convertLatLonToMGRS(this.state.lat, this.state.lon); - - this.setState({ mgrs, utm }); - } else { - this.setState({ - mgrs: '', - utm: { northing: '', easting: '', zoneNumber: '', zoneLetter: '', zone: '' }, - }); - } - }; - - /** - * Sync the current lat/lon to MGRS that is set - */ - _syncToMGRS = () => { - if (this.state.mgrs !== '') { - let lon; - let lat; - - try { - const { north, east } = convertMGRStoLL(this.state.mgrs); - lat = north; - lon = east; - } catch (err) { - return; - } - - const utm = convertLatLonToUTM(lat, lon); - - this.setState({ - lat: isNaN(lat) ? '' : lat, - lon: isNaN(lon) ? '' : lon, - utm, - }); - } else { - this.setState({ - lat: '', - lon: '', - utm: { northing: '', easting: '', zoneNumber: '', zoneLetter: '', zone: '' }, - }); - } - }; - - /** - * Sync the current lat/lon to UTM that is set - */ - _syncToUTM = () => { - if (this.state.utm) { - let lat; - let lon; - try { - ({ lat, lon } = converter.UTMtoLL( - this.state.utm.northing, - this.state.utm.easting, - this.state.utm.zoneNumber - )); - } catch (err) { - return; - } - - const mgrs = convertLatLonToMGRS(lat, lon); - - this.setState({ - lat: isNaN(lat) ? '' : lat, - lon: isNaN(lon) ? '' : lon, - mgrs, - }); - } else { - this.setState({ - lat: '', - lon: '', - mgrs: '', - }); - } - }; - - _renderNumberFormRow = ({ - value, - min, - max, - onChange, - label, - dataTestSubj, - }: { - value: string | number; - min: number; - max: number; - onChange: (evt: ChangeEvent) => void; - label: string; - dataTestSubj: string; - }) => { - const isInvalid = value === '' || value > max || value < min; - const error = isInvalid ? `Must be between ${min} and ${max}` : null; - return { - isInvalid, - component: ( - - - - ), - }; - }; - - _renderMGRSFormRow = ({ - value, - onChange, - label, - dataTestSubj, - }: { - value: string; - onChange: (evt: ChangeEvent) => void; - label: string; - dataTestSubj: string; - }) => { - let point; - try { - point = convertMGRStoLL(value); - } catch (err) { - point = undefined; - } - - const isInvalid = - value === '' || - point === undefined || - !point.north || - isNaN(point.north) || - !point.south || - isNaN(point.south) || - !point.east || - isNaN(point.east) || - !point.west || - isNaN(point.west); - const error = isInvalid - ? i18n.translate('xpack.maps.setViewControl.mgrsInvalid', { - defaultMessage: 'MGRS is invalid', - }) - : null; - return { - isInvalid, - component: ( - - - - ), - }; - }; - - _renderUTMZoneRow = ({ - value, - onChange, - label, - dataTestSubj, - }: { - value: string | number; - onChange: (evt: ChangeEvent) => void; - label: string; - dataTestSubj: string; - }) => { - let point; - try { - point = converter.UTMtoLL( - this.state.utm.northing, - this.state.utm.easting, - this.state.utm.zoneNumber - ); - } catch { - point = undefined; - } - - const isInvalid = value === '' || point === undefined; - const error = isInvalid - ? i18n.translate('xpack.maps.setViewControl.utmInvalidZone', { - defaultMessage: 'UTM Zone is invalid', - }) - : null; - return { - isInvalid, - component: ( - - - - ), - }; - }; - - _renderUTMEastingRow = ({ - value, - onChange, - label, - dataTestSubj, - }: { - value: string | number; - onChange: (evt: ChangeEvent) => void; - label: string; - dataTestSubj: string; - }) => { - let point; - try { - point = converter.UTMtoLL(this.state.utm.northing, value, this.state.utm.zoneNumber); - } catch { - point = undefined; - } - const isInvalid = value === '' || point === undefined; - const error = isInvalid - ? i18n.translate('xpack.maps.setViewControl.utmInvalidEasting', { - defaultMessage: 'UTM Easting is invalid', - }) - : null; - return { - isInvalid, - component: ( - - - - ), - }; - }; - - _renderUTMNorthingRow = ({ - value, - onChange, - label, - dataTestSubj, - }: { - value: string | number; - onChange: (evt: ChangeEvent) => void; - label: string; - dataTestSubj: string; - }) => { - let point; - try { - point = converter.UTMtoLL(value, this.state.utm.easting, this.state.utm.zoneNumber); - } catch { - point = undefined; - } - const isInvalid = value === '' || point === undefined; - const error = isInvalid - ? i18n.translate('xpack.maps.setViewControl.utmInvalidNorthing', { - defaultMessage: 'UTM Northing is invalid', - }) - : null; - return { - isInvalid, - component: ( - - - - ), - }; - }; - - _onSubmit = () => { - const { lat, lon, zoom } = this.state; + _onSubmit = (lat: number, lon: number, zoom: number) => { this._closePopover(); - this.props.onSubmit({ lat: lat as number, lon: lon as number, zoom: zoom as number }); + this.props.onSubmit({ lat, lon, zoom }); }; - _renderSetViewForm() { - let isLatInvalid; - let latFormRow; - let isLonInvalid; - let lonFormRow; - let isMGRSInvalid; - let mgrsFormRow; - let isUtmZoneInvalid; - let utmZoneRow; - let isUtmEastingInvalid; - let utmEastingRow; - let isUtmNorthingInvalid; - let utmNorthingRow; - - if (this.state.coord === COORDINATE_SYSTEM_DEGREES_DECIMAL) { - const latRenderObject = this._renderNumberFormRow({ - value: this.state.lat, - min: -90, - max: 90, - onChange: this._onLatChange, - label: i18n.translate('xpack.maps.setViewControl.latitudeLabel', { - defaultMessage: 'Latitude', - }), - dataTestSubj: 'latitudeInput', - }); - - isLatInvalid = latRenderObject.isInvalid; - latFormRow = latRenderObject.component; - - const lonRenderObject = this._renderNumberFormRow({ - value: this.state.lon, - min: -180, - max: 180, - onChange: this._onLonChange, - label: i18n.translate('xpack.maps.setViewControl.longitudeLabel', { - defaultMessage: 'Longitude', - }), - dataTestSubj: 'longitudeInput', - }); - - isLonInvalid = lonRenderObject.isInvalid; - lonFormRow = lonRenderObject.component; - } else if (this.state.coord === COORDINATE_SYSTEM_MGRS) { - const mgrsRenderObject = this._renderMGRSFormRow({ - value: this.state.mgrs, - onChange: this._onMGRSChange, - label: i18n.translate('xpack.maps.setViewControl.mgrsLabel', { - defaultMessage: 'MGRS', - }), - dataTestSubj: 'mgrsInput', - }); - - isMGRSInvalid = mgrsRenderObject.isInvalid; - mgrsFormRow = mgrsRenderObject.component; - } else if (this.state.coord === COORDINATE_SYSTEM_UTM) { - const utmZoneRenderObject = this._renderUTMZoneRow({ - value: this.state.utm !== undefined ? this.state.utm.zone : '', - onChange: this._onUTMZoneChange, - label: i18n.translate('xpack.maps.setViewControl.utmZoneLabel', { - defaultMessage: 'UTM Zone', - }), - dataTestSubj: 'utmZoneInput', - }); - - isUtmZoneInvalid = utmZoneRenderObject.isInvalid; - utmZoneRow = utmZoneRenderObject.component; - - const utmEastingRenderObject = this._renderUTMEastingRow({ - value: this.state.utm !== undefined ? this.state.utm.easting : '', - onChange: this._onUTMEastingChange, - label: i18n.translate('xpack.maps.setViewControl.utmEastingLabel', { - defaultMessage: 'UTM Easting', - }), - dataTestSubj: 'utmEastingInput', - }); - - isUtmEastingInvalid = utmEastingRenderObject.isInvalid; - utmEastingRow = utmEastingRenderObject.component; - - const utmNorthingRenderObject = this._renderUTMNorthingRow({ - value: this.state.utm !== undefined ? this.state.utm.northing : '', - onChange: this._onUTMNorthingChange, - label: i18n.translate('xpack.maps.setViewControl.utmNorthingLabel', { - defaultMessage: 'UTM Northing', - }), - dataTestSubj: 'utmNorthingInput', - }); - - isUtmNorthingInvalid = utmNorthingRenderObject.isInvalid; - utmNorthingRow = utmNorthingRenderObject.component; - } - - const { isInvalid: isZoomInvalid, component: zoomFormRow } = this._renderNumberFormRow({ - value: this.state.zoom, - min: this.props.settings.minZoom, - max: this.props.settings.maxZoom, - onChange: this._onZoomChange, - label: i18n.translate('xpack.maps.setViewControl.zoomLabel', { - defaultMessage: 'Zoom', - }), - dataTestSubj: 'zoomInput', - }); - - let coordinateInputs; - if (this.state.coord === 'dd') { - coordinateInputs = ( - - {latFormRow} - {lonFormRow} - {zoomFormRow} - - ); - } else if (this.state.coord === 'dms') { - coordinateInputs = ( - - {latFormRow} - {lonFormRow} - {zoomFormRow} - - ); - } else if (this.state.coord === 'utm') { - coordinateInputs = ( - - {utmZoneRow} - {utmEastingRow} - {utmNorthingRow} - {zoomFormRow} - - ); - } else if (this.state.coord === 'mgrs') { - coordinateInputs = ( - - {mgrsFormRow} - {zoomFormRow} - - ); - } - - return ( - - { - this.setState({ isCoordPopoverOpen: false }); - }} - button={ - { - this.setState({ isCoordPopoverOpen: !this.state.isCoordPopoverOpen }); - }} - > - Coordinate System - - } - > - - - - {coordinateInputs} - - - - - - - - - - ); - } - render() { return ( { isOpen={this.state.isPopoverOpen} closePopover={this._closePopover} > - {this._renderSetViewForm()} + ); } } - -function convertLatLonToUTM(lat: string | number, lon: string | number) { - const utmCoord = converter.LLtoUTM(lat, lon); - - let eastwest = 'E'; - if (utmCoord.easting < 0) { - eastwest = 'W'; - } - let norwest = 'N'; - if (utmCoord.northing < 0) { - norwest = 'S'; - } - - if (utmCoord !== 'undefined') { - utmCoord.zoneLetter = isNaN(lat) ? '' : converter.UTMLetterDesignator(lat); - utmCoord.zone = `${utmCoord.zoneNumber}${utmCoord.zoneLetter}`; - utmCoord.easting = Math.round(utmCoord.easting); - utmCoord.northing = Math.round(utmCoord.northing); - utmCoord.str = `${utmCoord.zoneNumber}${utmCoord.zoneLetter} ${utmCoord.easting}${eastwest} ${utmCoord.northing}${norwest}`; - } - - return utmCoord; -} - -function convertLatLonToMGRS(lat: string | number, lon: string | number) { - const mgrsCoord = converter.LLtoMGRS(lat, lon, 5); - return mgrsCoord; -} - -function getViewString(lat: number, lon: number, zoom: number) { - return `${lat},${lon},${zoom}`; -} - -function convertMGRStoUSNG(mgrs: string) { - let squareIdEastSpace = 0; - for (let i = mgrs.length - 1; i > -1; i--) { - // check if we have hit letters yet - if (isNaN(mgrs.substr(i, 1))) { - squareIdEastSpace = i + 1; - break; - } - } - const gridZoneSquareIdSpace = squareIdEastSpace ? squareIdEastSpace - 2 : -1; - const numPartLength = mgrs.substr(squareIdEastSpace).length / 2; - // add the number split space - const eastNorthSpace = squareIdEastSpace ? squareIdEastSpace + numPartLength : -1; - const stringArray = mgrs.split(''); - - stringArray.splice(eastNorthSpace, 0, ' '); - stringArray.splice(squareIdEastSpace, 0, ' '); - stringArray.splice(gridZoneSquareIdSpace, 0, ' '); - - const rejoinedArray = stringArray.join(''); - return rejoinedArray; -} - -function convertMGRStoLL(mgrs: string) { - return mgrs ? converter.USNGtoLL(convertMGRStoUSNG(mgrs)) : ''; -} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_form.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_form.tsx new file mode 100644 index 000000000000..28fe6073d764 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_form.tsx @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { Component } from 'react'; +import { EuiButtonEmpty, EuiPopover, EuiRadioGroup } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MapCenter, MapSettings } from '../../../../common/descriptor_types'; +import { DecimalDegreesForm } from './decimal_degrees_form'; +import { MgrsForm } from './mgrs_form'; +import { UtmForm } from './utm_form'; + +const DEGREES_DECIMAL = 'dd'; +const MGRS = 'mgrs'; +const UTM = 'utm'; + +const COORDINATE_SYSTEM_OPTIONS = [ + { + id: DEGREES_DECIMAL, + label: i18n.translate('xpack.maps.setViewControl.decimalDegreesLabel', { + defaultMessage: 'Decimal degrees', + }), + }, + { + id: UTM, + label: 'UTM', + }, + { + id: MGRS, + label: 'MGRS', + }, +]; + +interface Props { + settings: MapSettings; + zoom: number; + center: MapCenter; + onSubmit: (lat: number, lon: number, zoom: number) => void; +} + +interface State { + isPopoverOpen: boolean; + coordinateSystem: string; +} + +export class SetViewForm extends Component { + state: State = { + coordinateSystem: DEGREES_DECIMAL, + isPopoverOpen: false, + }; + + _togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + _closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + _onCoordinateSystemChange = (optionId: string) => { + this._closePopover(); + this.setState({ + coordinateSystem: optionId, + }); + }; + + _renderForm() { + if (this.state.coordinateSystem === MGRS) { + return ( + + ); + } + + if (this.state.coordinateSystem === UTM) { + return ( + + ); + } + + return ( + + ); + } + + render() { + return ( +
    + + + + } + > + + + {this._renderForm()} +
    + ); + } +} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.test.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.test.ts new file mode 100644 index 000000000000..e6a6819687d1 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ddToMGRS, mgrsToDD, ddToUTM, utmToDD } from './utils'; + +describe('MGRS', () => { + test('ddToMGRS should convert lat lon to MGRS', () => { + expect(ddToMGRS(29.29926, 32.05495)).toEqual('36RVT08214151'); + }); + + test('ddToMGRS should return empty string for lat lon that does not translate to MGRS grid', () => { + expect(ddToMGRS(90, 32.05495)).toEqual(''); + }); + + test('mgrsToDD should convert MGRS to lat lon', () => { + expect(mgrsToDD('36RVT08214151')).toEqual({ + east: 32.05498649594143, + north: 29.299330195900975, + south: 29.299239224067065, + west: 32.054884373627345, + }); + }); +}); + +describe('UTM', () => { + test('ddToUTM should convert lat lon to UTM', () => { + expect(ddToUTM(29.29926, 32.05495)).toEqual({ + easting: '408216', + northing: '3241512', + zone: '36R', + }); + }); + + test('ddToUTM should return empty strings for lat lon that does not translate to UTM grid', () => { + expect(ddToUTM(90, 32.05495)).toEqual({ + northing: '', + easting: '', + zone: '', + }); + }); + + test('utmToDD should convert UTM to lat lon', () => { + expect(utmToDD('3241512', '408216', '36R')).toEqual({ + lat: 29.29925770984472, + lon: 32.05494597943409, + }); + }); +}); diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.ts new file mode 100644 index 000000000000..7edf1428d931 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utils.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 * as usng from 'usng.js'; + +// @ts-ignore +const converter = new usng.Converter(); + +export function withinRange(value: string | number, min: number, max: number) { + const isInvalid = value === '' || value > max || value < min; + const error = isInvalid + ? i18n.translate('xpack.maps.setViewControl.outOfRangeErrorMsg', { + defaultMessage: `Must be between {min} and {max}`, + values: { min, max }, + }) + : null; + return { isInvalid, error }; +} + +export function ddToUTM(lat: number, lon: number) { + try { + const utm = converter.LLtoUTM(lat, lon); + return { + northing: utm === converter.UNDEFINED_STR ? '' : String(Math.round(utm.northing)), + easting: utm === converter.UNDEFINED_STR ? '' : String(Math.round(utm.easting)), + zone: + utm === converter.UNDEFINED_STR + ? '' + : `${utm.zoneNumber}${converter.UTMLetterDesignator(lat)}`, + }; + } catch (e) { + return { + northing: '', + easting: '', + zone: '', + }; + } +} + +export function utmToDD(northing: string, easting: string, zoneNumber: string) { + try { + return converter.UTMtoLL(northing, easting, zoneNumber); + } catch (e) { + return undefined; + } +} + +export function ddToMGRS(lat: number, lon: number) { + try { + const mgrsCoord = converter.LLtoMGRS(lat, lon, 5); + return mgrsCoord; + } catch (e) { + return ''; + } +} + +function mgrstoUSNG(mgrs: string) { + let squareIdEastSpace = 0; + for (let i = mgrs.length - 1; i > -1; i--) { + // check if we have hit letters yet + if (isNaN(parseInt(mgrs.substr(i, 1), 10))) { + squareIdEastSpace = i + 1; + break; + } + } + const gridZoneSquareIdSpace = squareIdEastSpace ? squareIdEastSpace - 2 : -1; + const numPartLength = mgrs.substr(squareIdEastSpace).length / 2; + // add the number split space + const eastNorthSpace = squareIdEastSpace ? squareIdEastSpace + numPartLength : -1; + const stringArray = mgrs.split(''); + + stringArray.splice(eastNorthSpace, 0, ' '); + stringArray.splice(squareIdEastSpace, 0, ' '); + stringArray.splice(gridZoneSquareIdSpace, 0, ' '); + + const rejoinedArray = stringArray.join(''); + return rejoinedArray; +} + +export function mgrsToDD(mgrs: string) { + try { + return converter.USNGtoLL(mgrstoUSNG(mgrs)); + } catch (e) { + return undefined; + } +} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utm_form.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utm_form.tsx new file mode 100644 index 000000000000..76a362821705 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/utm_form.tsx @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 _ from 'lodash'; +import React, { ChangeEvent, Component } from 'react'; +import { + EuiForm, + EuiFormRow, + EuiButton, + EuiFieldNumber, + EuiFieldText, + EuiTextAlign, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MapCenter, MapSettings } from '../../../../common/descriptor_types'; +import { ddToUTM, utmToDD, withinRange } from './utils'; + +interface Props { + settings: MapSettings; + zoom: number; + center: MapCenter; + onSubmit: (lat: number, lon: number, zoom: number) => void; +} + +interface State { + northing: string; + easting: string; + zone: string; + zoom: number | string; +} + +export class UtmForm extends Component { + constructor(props: Props) { + super(props); + const utm = ddToUTM(this.props.center.lat, this.props.center.lon); + this.state = { + northing: utm.northing, + easting: utm.easting, + zone: utm.zone, + zoom: this.props.zoom, + }; + } + + _toPoint() { + const { northing, easting, zone } = this.state; + return northing === '' || easting === '' || zone.length < 2 + ? undefined + : utmToDD(northing, easting, zone.substring(0, zone.length - 1)); + } + + _isUtmInvalid() { + const point = this._toPoint(); + return point === undefined; + } + + _onZoneChange = (evt: ChangeEvent) => { + this.setState({ + zone: _.isNull(evt.target.value) ? '' : evt.target.value, + }); + }; + + _onEastingChange = (evt: ChangeEvent) => { + this.setState({ + easting: _.isNull(evt.target.value) ? '' : evt.target.value, + }); + }; + + _onNorthingChange = (evt: ChangeEvent) => { + this.setState({ + northing: _.isNull(evt.target.value) ? '' : evt.target.value, + }); + }; + + _onZoomChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + zoom: isNaN(sanitizedValue) ? '' : sanitizedValue, + }); + }; + + _onSubmit = () => { + const point = this._toPoint(); + if (point) { + this.props.onSubmit(point.lat, point.lon, this.state.zoom as number); + } + }; + + render() { + const isUtmInvalid = this._isUtmInvalid(); + const northingError = + isUtmInvalid || this.state.northing === '' + ? i18n.translate('xpack.maps.setViewControl.utmInvalidNorthing', { + defaultMessage: 'UTM Northing is invalid', + }) + : null; + const eastingError = + isUtmInvalid || this.state.northing === '' + ? i18n.translate('xpack.maps.setViewControl.utmInvalidEasting', { + defaultMessage: 'UTM Easting is invalid', + }) + : null; + const zoneError = + isUtmInvalid || this.state.northing === '' + ? i18n.translate('xpack.maps.setViewControl.utmInvalidZone', { + defaultMessage: 'UTM Zone is invalid', + }) + : null; + const { isInvalid: isZoomInvalid, error: zoomError } = withinRange( + this.state.zoom, + this.props.settings.minZoom, + this.props.settings.maxZoom + ); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } +} From 427637a3b02a48a347409f77eaa34e4f53541970 Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Tue, 27 Sep 2022 18:05:56 -0600 Subject: [PATCH 043/185] [RAM] Fix bulk editing snooze schedule overwriting existing snooze (#141883) * Fix odd behaviour with bulk snoozing * Fix redundant code Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/rules_client/rules_client.ts | 52 ++- .../rules_client/tests/bulk_edit.test.ts | 300 ++++++++++++++---- 2 files changed, 283 insertions(+), 69 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index ed330dbd22e0..ce1dfcc1827b 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1797,7 +1797,7 @@ export class RulesClient { break; } if (operation.operation === 'set') { - const snoozeAttributes = getSnoozeAttributes(attributes, operation.value); + const snoozeAttributes = getBulkSnoozeAttributes(attributes, operation.value); try { verifySnoozeScheduleLimit(snoozeAttributes); } catch (error) { @@ -1819,7 +1819,7 @@ export class RulesClient { } attributes = { ...attributes, - ...getUnsnoozeAttributes(attributes, idsToDelete), + ...getBulkUnsnoozeAttributes(attributes, idsToDelete), }; } break; @@ -3254,6 +3254,33 @@ function getSnoozeAttributes(attributes: RawRule, snoozeSchedule: RuleSnoozeSche }; } +function getBulkSnoozeAttributes(attributes: RawRule, snoozeSchedule: RuleSnoozeSchedule) { + // If duration is -1, instead mute all + const { id: snoozeId, duration } = snoozeSchedule; + + if (duration === -1) { + return { + muteAll: true, + snoozeSchedule: clearUnscheduledSnooze(attributes), + }; + } + + // Bulk adding snooze schedule, don't touch the existing snooze/indefinite snooze + if (snoozeId) { + const existingSnoozeSchedules = attributes.snoozeSchedule || []; + return { + muteAll: attributes.muteAll, + snoozeSchedule: [...existingSnoozeSchedules, snoozeSchedule], + }; + } + + // Bulk snoozing, don't touch the existing snooze schedules + return { + muteAll: false, + snoozeSchedule: [...clearUnscheduledSnooze(attributes), snoozeSchedule], + }; +} + function getUnsnoozeAttributes(attributes: RawRule, scheduleIds?: string[]) { const snoozeSchedule = scheduleIds ? clearScheduledSnoozesById(attributes, scheduleIds) @@ -3265,6 +3292,27 @@ function getUnsnoozeAttributes(attributes: RawRule, scheduleIds?: string[]) { }; } +function getBulkUnsnoozeAttributes(attributes: RawRule, scheduleIds?: string[]) { + // Bulk removing snooze schedules, don't touch the current snooze/indefinite snooze + if (scheduleIds) { + const newSchedules = clearScheduledSnoozesById(attributes, scheduleIds); + // Unscheduled snooze is also known as snooze now + const unscheduledSnooze = + attributes.snoozeSchedule?.filter((s) => typeof s.id === 'undefined') || []; + + return { + snoozeSchedule: [...unscheduledSnooze, ...newSchedules], + muteAll: attributes.muteAll, + }; + } + + // Bulk unsnoozing, don't touch current snooze schedules that are NOT active + return { + snoozeSchedule: clearCurrentActiveSnooze(attributes), + muteAll: false, + }; +} + function clearUnscheduledSnooze(attributes: RawRule) { // Clear any snoozes that have no ID property. These are "simple" snoozes created with the quick UI, e.g. snooze for 3 days starting now return attributes.snoozeSchedule diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index 1cafcd652349..5e440d2e6b6d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -26,9 +26,11 @@ jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation })); jest.mock('../../lib/snooze/is_snooze_active', () => ({ - isSnoozeActive: jest.fn(() => true), + isSnoozeActive: jest.fn(), })); +const { isSnoozeActive } = jest.requireMock('../../lib/snooze/is_snooze_active'); + const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); @@ -310,9 +312,13 @@ describe('bulkEdit()', () => { }); describe('snoozeSchedule operations', () => { - const getSnoozeSchedule = () => { + afterEach(() => { + isSnoozeActive.mockImplementation(() => false); + }); + + const getSnoozeSchedule = (useId: boolean = true) => { return { - id: uuid.v4(), + ...(useId && { id: uuid.v4() }), duration: 28800000, rRule: { dtstart: '2010-09-19T11:49:59.329Z', @@ -321,8 +327,10 @@ describe('bulkEdit()', () => { }, }; }; - test('should add snooze', async () => { - unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const getMockAttribute = (override: Record = {}) => { + return { saved_objects: [ { id: '1', @@ -339,15 +347,137 @@ describe('bulkEdit()', () => { notifyWhen: null, actions: [], snoozeSchedule: [], + ...override, }, references: [], version: '123', }, ], + }; + }; + + test('should snooze', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + const snoozePayload = getSnoozeSchedule(false); + await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + operation: 'set', + field: 'snoozeSchedule', + value: snoozePayload, + }, + ], + }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + snoozeSchedule: [snoozePayload], + }), + }), + ], + { overwrite: true } + ); + }); + + test('should add snooze schedule', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + + const snoozePayload = getSnoozeSchedule(); + await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + operation: 'set', + field: 'snoozeSchedule', + value: snoozePayload, + }, + ], + }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + snoozeSchedule: [snoozePayload], + }), + }), + ], + { overwrite: true } + ); + }); + + test('should not unsnooze a snoozed rule when bulk adding snooze schedules', async () => { + const existingSnooze = [getSnoozeSchedule(false), getSnoozeSchedule()]; + + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [ + { + ...existingDecryptedRule, + attributes: { + ...existingDecryptedRule.attributes, + snoozeSchedule: existingSnooze, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + }, + ], }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + const snoozePayload = getSnoozeSchedule(); + await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + operation: 'set', + field: 'snoozeSchedule', + value: snoozePayload, + }, + ], + }); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: '1', + type: 'alert', + attributes: expect.objectContaining({ + snoozeSchedule: [...existingSnooze, snoozePayload], + }), + }), + ], + { overwrite: true } + ); + }); + + test('should not unsnooze an indefinitely snoozed rule when bulk adding snooze schedules', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [ + { + ...existingDecryptedRule, + attributes: { + ...existingDecryptedRule.attributes, + muteAll: true, + snoozeSchedule: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + }, + ], + }); + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + + const snoozePayload = getSnoozeSchedule(); await rulesClient.bulkEdit({ filter: '', operations: [ @@ -366,6 +496,7 @@ describe('bulkEdit()', () => { id: '1', type: 'alert', attributes: expect.objectContaining({ + muteAll: true, snoozeSchedule: [snoozePayload], }), }), @@ -374,39 +505,74 @@ describe('bulkEdit()', () => { ); }); - test('should delete snooze', async () => { - const existingSnooze = [getSnoozeSchedule(), getSnoozeSchedule()]; + test('should unsnooze', async () => { + const existingSnooze = [getSnoozeSchedule(false), getSnoozeSchedule(), getSnoozeSchedule()]; - unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [ { + ...existingDecryptedRule, + attributes: { + ...existingDecryptedRule.attributes, + snoozeSchedule: existingSnooze, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + }, + ], + }); + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + + await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + operation: 'delete', + field: 'snoozeSchedule', + }, + ], + }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ id: '1', type: 'alert', + attributes: expect.objectContaining({ + snoozeSchedule: [existingSnooze[1], existingSnooze[2]], + }), + }), + ], + { overwrite: true } + ); + }); + + test('should remove snooze schedules', async () => { + const existingSnooze = [getSnoozeSchedule(), getSnoozeSchedule()]; + + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [ + { + ...existingDecryptedRule, attributes: { - enabled: true, - tags: ['foo', 'test-1'], - alertTypeId: 'myType', - schedule: { interval: '1m' }, - consumer: 'myApp', - scheduledTaskId: 'task-123', - params: {}, - throttle: null, - notifyWhen: null, - actions: [], + ...existingDecryptedRule.attributes, snoozeSchedule: existingSnooze, - }, - references: [], - version: '123', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, }, ], }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + await rulesClient.bulkEdit({ filter: '', operations: [ { operation: 'delete', field: 'snoozeSchedule', + value: [], }, ], }); @@ -426,14 +592,8 @@ describe('bulkEdit()', () => { ); }); - test('should error if adding snooze schedule to rule with 5 schedules', async () => { - const existingSnooze = [ - getSnoozeSchedule(), - getSnoozeSchedule(), - getSnoozeSchedule(), - getSnoozeSchedule(), - getSnoozeSchedule(), - ]; + test('should not unsnooze rule when removing snooze schedules', async () => { + const existingSnooze = [getSnoozeSchedule(false), getSnoozeSchedule(), getSnoozeSchedule()]; mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [ @@ -448,30 +608,58 @@ describe('bulkEdit()', () => { ], }); - unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [ + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + + await rulesClient.bulkEdit({ + filter: '', + operations: [ { + operation: 'delete', + field: 'snoozeSchedule', + value: [], + }, + ], + }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + expect.objectContaining({ id: '1', type: 'alert', + attributes: expect.objectContaining({ + snoozeSchedule: [existingSnooze[0]], + }), + }), + ], + { overwrite: true } + ); + }); + + test('should error if adding snooze schedule to rule with 5 schedules', async () => { + const existingSnooze = [ + getSnoozeSchedule(), + getSnoozeSchedule(), + getSnoozeSchedule(), + getSnoozeSchedule(), + getSnoozeSchedule(), + ]; + + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [ + { + ...existingDecryptedRule, attributes: { - enabled: true, - tags: ['foo', 'test-1'], - alertTypeId: 'myType', - schedule: { interval: '1m' }, - consumer: 'myApp', - scheduledTaskId: 'task-123', - params: {}, - throttle: null, - notifyWhen: null, - actions: [], + ...existingDecryptedRule.attributes, snoozeSchedule: existingSnooze, - }, - references: [], - version: '123', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, }, ], }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); + const snoozePayload = getSnoozeSchedule(); const response = await rulesClient.bulkEdit({ @@ -501,29 +689,7 @@ describe('bulkEdit()', () => { ], }); - unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [ - { - id: '1', - type: 'alert', - attributes: { - enabled: true, - tags: ['foo', 'test-1'], - alertTypeId: 'myType', - schedule: { interval: '1m' }, - consumer: 'myApp', - scheduledTaskId: 'task-123', - params: {}, - throttle: null, - notifyWhen: null, - actions: [], - snoozeSchedule: [], - }, - references: [], - version: '123', - }, - ], - }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue(getMockAttribute()); const snoozePayload = getSnoozeSchedule(); From a8e8f3f4c58f1caa0165a2cb1669729c8b177736 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau Date: Tue, 27 Sep 2022 20:19:39 -0400 Subject: [PATCH 044/185] fix filter on kuery node (#142001) --- .../alerting/server/rules_client/rules_client.ts | 2 +- .../server/rules_client/tests/find.test.ts | 6 ++++-- .../spaces_only/tests/alerting/find.ts | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index ce1dfcc1827b..a8367d9ff52b 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1110,7 +1110,7 @@ export class RulesClient { filter: (authorizationFilter && filterKueryNode ? nodeBuilder.and([filterKueryNode, authorizationFilter as KueryNode]) - : authorizationFilter) ?? options.filter, + : authorizationFilter) ?? filterKueryNode, fields: fields ? this.includeFieldsRequiredForAuthentication(fields) : fields, type: 'alert', }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index e26004de99a3..4cfa13f69b84 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -176,7 +176,7 @@ describe('find()', () => { Array [ Object { "fields": undefined, - "filter": undefined, + "filter": null, "sortField": undefined, "type": "alert", }, @@ -277,7 +277,7 @@ describe('find()', () => { Array [ Object { "fields": undefined, - "filter": undefined, + "filter": null, "sortField": undefined, "type": "alert", }, @@ -730,6 +730,8 @@ describe('find()', () => { expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledWith({ fields: ['tags', 'alertTypeId', 'consumer'], + filter: null, + sortField: undefined, type: 'alert', }); expect(ensureRuleTypeIsAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'rule'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 2d30465401ca..23dcc1abaea4 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { SuperTest, Test } 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'; @@ -198,6 +199,20 @@ const findTestUtils = ( expect(response.body.data[0].params.strValue).to.eql('my b'); }); + 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( + 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.get( `${getUrlPrefix(Spaces.space1.id)}/${ From ace4f42f598a2e8657c2edcfd173d2b4b0de5529 Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Tue, 27 Sep 2022 20:08:28 -0700 Subject: [PATCH 045/185] [RAM] Fix bulk snooze select all filter not matching rules list filter (#141996) * Select all filter now correctly match the rules list filter * Improve filter kuery node logic * unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/rules_client/rules_client.ts | 11 +- .../update_api_key_modal_confirmation.tsx | 9 +- .../hooks/use_bulk_edit_select.test.tsx | 131 ++++++++++++++++++ .../hooks/use_bulk_edit_select.tsx | 68 ++++++++- .../public/application/lib/rule_api/snooze.ts | 5 +- .../application/lib/rule_api/unsnooze.ts | 5 +- .../lib/rule_api/update_api_key.ts | 5 +- .../rule_quick_edit_buttons.test.tsx | 10 +- .../components/rule_quick_edit_buttons.tsx | 13 +- .../components/bulk_snooze_modal.tsx | 9 +- .../components/bulk_snooze_schedule_modal.tsx | 9 +- .../rules_list/components/rules_list.tsx | 38 +++-- 12 files changed, 259 insertions(+), 54 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index a8367d9ff52b..c2a643ce8c29 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -22,7 +22,7 @@ import { } from 'lodash'; import { i18n } from '@kbn/i18n'; import { AlertConsumers } from '@kbn/rule-data-utils'; -import { fromKueryExpression, KueryNode, nodeBuilder } from '@kbn/es-query'; +import { KueryNode, nodeBuilder } from '@kbn/es-query'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Logger, @@ -1569,14 +1569,7 @@ export class RulesClient { ); } - let qNodeQueryFilter: null | KueryNode; - if (!queryFilter) { - qNodeQueryFilter = null; - } else if (typeof queryFilter === 'string') { - qNodeQueryFilter = fromKueryExpression(queryFilter); - } else { - qNodeQueryFilter = queryFilter; - } + const qNodeQueryFilter = buildKueryNodeFilter(queryFilter); const qNodeFilter = ids ? convertRuleIdsToKueryNode(ids) : qNodeQueryFilter; let authorizationTuple; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/update_api_key_modal_confirmation.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/update_api_key_modal_confirmation.tsx index 0c6f3edaf3b8..c3072c123ff8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/update_api_key_modal_confirmation.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/update_api_key_modal_confirmation.tsx @@ -6,6 +6,7 @@ */ import { EuiConfirmModal } from '@elastic/eui'; +import { KueryNode } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState, useMemo } from 'react'; import { HttpSetup } from '@kbn/core/public'; @@ -25,7 +26,7 @@ export const UpdateApiKeyModalConfirmation = ({ }: { onCancel: () => void; idsToUpdate: string[]; - idsToUpdateFilter?: string; + idsToUpdateFilter?: KueryNode | null | undefined; numberOfSelectedRules?: number; apiUpdateApiKeyCall: ({ ids, @@ -33,7 +34,7 @@ export const UpdateApiKeyModalConfirmation = ({ filter, }: { ids?: string[]; - filter?: string; + filter?: KueryNode | null | undefined; http: HttpSetup; }) => Promise; setIsLoadingState: (isLoading: boolean) => void; @@ -50,7 +51,7 @@ export const UpdateApiKeyModalConfirmation = ({ const { showToast } = useBulkEditResponse({ onSearchPopulate }); useEffect(() => { - if (idsToUpdateFilter) { + if (typeof idsToUpdateFilter !== 'undefined') { setUpdateModalVisibility(true); } else { setUpdateModalVisibility(idsToUpdate.length > 0); @@ -58,7 +59,7 @@ export const UpdateApiKeyModalConfirmation = ({ }, [idsToUpdate, idsToUpdateFilter]); const numberOfIdsToUpdate = useMemo(() => { - if (idsToUpdateFilter) { + if (typeof idsToUpdateFilter !== 'undefined') { return numberOfSelectedRules; } return idsToUpdate.length; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx new file mode 100644 index 000000000000..b0e92a3d3a84 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { useBulkEditSelect } from './use_bulk_edit_select'; +import { RuleTableItem } from '../../types'; + +const items = [ + { + id: '1', + isEditable: true, + }, + { + id: '2', + isEditable: true, + }, + { + id: '3', + isEditable: true, + }, + { + id: '4', + isEditable: true, + }, +] as RuleTableItem[]; + +describe('useBulkEditSelectTest', () => { + it('getFilter should return null when nothing is selected', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + }) + ); + + expect(result.current.getFilter()).toEqual(null); + }); + + it('getFilter should return rule list filter when nothing is selected', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + tagsFilter: ['test: 123'], + searchText: 'rules*', + }) + ); + + expect(result.current.getFilter()?.arguments.length).toEqual(2); + }); + + it('getFilter should return rule list filter when something is selected', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + tagsFilter: ['test: 123'], + searchText: 'rules*', + }) + ); + + act(() => { + result.current.onSelectRow(items[0]); + }); + + expect(result.current.getFilter()?.arguments.length).toEqual(2); + expect([...result.current.selectedIds]).toEqual([items[0].id]); + }); + + it('getFilter should return null when selecting all', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + }) + ); + + act(() => { + result.current.onSelectAll(); + }); + + expect(result.current.getFilter()).toEqual(null); + }); + + it('getFilter should return rule list filter when selecting all', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + tagsFilter: ['test: 123'], + searchText: 'rules*', + }) + ); + + act(() => { + result.current.onSelectAll(); + }); + + expect(result.current.getFilter()?.arguments.length).toEqual(2); + }); + + it('getFilter should return rule list filter and exclude ids when selecting all with excluded ids', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + tagsFilter: ['test: 123'], + searchText: 'rules*', + }) + ); + + act(() => { + result.current.onSelectAll(); + result.current.onSelectRow(items[0]); + }); + + expect(result.current.getFilter()?.arguments.length).toEqual(2); + expect(result.current.getFilter()?.arguments[1].arguments[0].arguments).toEqual([ + expect.objectContaining({ + value: 'alert.id', + }), + expect.objectContaining({ + value: 'alert:1', + }), + ]); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx index 20950d35b026..1e2ff7496907 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx @@ -5,7 +5,9 @@ * 2.0. */ import { useReducer, useMemo, useCallback } from 'react'; -import { RuleTableItem } from '../../types'; +import { fromKueryExpression, nodeBuilder } from '@kbn/es-query'; +import { mapFiltersToKueryNode } from '../lib/rule_api/map_filters_to_kuery_node'; +import { RuleTableItem, RuleStatus } from '../../types'; interface BulkEditSelectionState { selectedIds: Set; @@ -71,9 +73,26 @@ const reducer = (state: BulkEditSelectionState, action: Action) => { interface UseBulkEditSelectProps { totalItemCount: number; items: RuleTableItem[]; + typesFilter?: string[]; + actionTypesFilter?: string[]; + tagsFilter?: string[]; + ruleExecutionStatusesFilter?: string[]; + ruleStatusesFilter?: RuleStatus[]; + searchText?: string; } -export function useBulkEditSelect({ totalItemCount = 0, items = [] }: UseBulkEditSelectProps) { +export function useBulkEditSelect(props: UseBulkEditSelectProps) { + const { + totalItemCount = 0, + items = [], + typesFilter, + actionTypesFilter, + tagsFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + searchText, + } = props; + const [state, dispatch] = useReducer(reducer, initialState); const itemIds = useMemo(() => { @@ -161,18 +180,55 @@ export function useBulkEditSelect({ totalItemCount = 0, items = [] }: UseBulkEdi dispatch({ type: ActionTypes.CLEAR_SELECTION }); }, []); + const getFilterKueryNode = useCallback( + (idsToExclude?: string[]) => { + const ruleFilterKueryNode = mapFiltersToKueryNode({ + typesFilter, + actionTypesFilter, + tagsFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + searchText, + }); + + if (idsToExclude && idsToExclude.length) { + if (ruleFilterKueryNode) { + return nodeBuilder.and([ + ruleFilterKueryNode, + fromKueryExpression( + `NOT (${idsToExclude.map((id) => `alert.id: "alert:${id}"`).join(' or ')})` + ), + ]); + } + } + + return ruleFilterKueryNode; + }, + [ + typesFilter, + actionTypesFilter, + tagsFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + searchText, + ] + ); + const getFilter = useCallback(() => { const { selectedIds, isAllSelected } = state; const idsArray = [...selectedIds]; if (isAllSelected) { + // Select all but nothing is selected to exclude if (idsArray.length === 0) { - return 'alert.id: *'; + return getFilterKueryNode(); } - return `NOT (${idsArray.map((id) => `alert.id: "alert:${id}"`).join(' or ')})`; + // Select all, exclude certain alerts + return getFilterKueryNode(idsArray); } - return ''; - }, [state]); + + return getFilterKueryNode(); + }, [state, getFilterKueryNode]); return useMemo(() => { return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts index 1f020aff106f..b82efdb5e09c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts @@ -5,6 +5,7 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; +import { KueryNode } from '@kbn/es-query'; import { SnoozeSchedule, BulkEditResponse } from '../../../types'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; @@ -37,7 +38,7 @@ export async function snoozeRule({ export interface BulkSnoozeRulesProps { ids?: string[]; - filter?: string; + filter?: KueryNode | null | undefined; snoozeSchedule: SnoozeSchedule; } @@ -51,7 +52,7 @@ export function bulkSnoozeRules({ try { body = JSON.stringify({ ids: ids?.length ? ids : undefined, - filter, + ...(filter ? { filter: JSON.stringify(filter) } : {}), operations: [ { operation: 'set', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts index 7bfadf1e1b82..d055149fdfbd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts @@ -5,6 +5,7 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; +import { KueryNode } from '@kbn/es-query'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { BulkEditResponse } from '../../../types'; @@ -26,7 +27,7 @@ export async function unsnoozeRule({ export interface BulkUnsnoozeRulesProps { ids?: string[]; - filter?: string; + filter?: KueryNode | null | undefined; scheduleIds?: string[]; } @@ -40,7 +41,7 @@ export function bulkUnsnoozeRules({ try { body = JSON.stringify({ ids: ids?.length ? ids : undefined, - filter, + ...(filter ? { filter: JSON.stringify(filter) } : {}), operations: [ { operation: 'delete', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts index 53327bbdb1e1..f9a6912c1d43 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts @@ -5,6 +5,7 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; +import { KueryNode } from '@kbn/es-query'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { BulkEditResponse } from '../../../types'; @@ -16,7 +17,7 @@ export async function updateAPIKey({ id, http }: { id: string; http: HttpSetup } export interface BulkUpdateAPIKeyProps { ids?: string[]; - filter?: string; + filter?: KueryNode | null | undefined; } export function bulkUpdateAPIKey({ @@ -28,7 +29,7 @@ export function bulkUpdateAPIKey({ try { body = JSON.stringify({ ids: ids?.length ? ids : undefined, - filter, + ...(filter ? { filter: JSON.stringify(filter) } : {}), operations: [ { operation: 'set', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx index 1605f09ebc0f..fcf1ba99b7af 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.test.tsx @@ -43,7 +43,7 @@ describe('rule_quick_edit_buttons', () => { const wrapper = mountWithIntl( ''} + getFilter={() => null} selectedItems={[mockRule]} onPerformingAction={() => {}} onActionPerformed={() => {}} @@ -80,7 +80,7 @@ describe('rule_quick_edit_buttons', () => { const wrapper = mountWithIntl( ''} + getFilter={() => null} selectedItems={[mockRule]} onPerformingAction={() => {}} onActionPerformed={() => {}} @@ -111,7 +111,7 @@ describe('rule_quick_edit_buttons', () => { const wrapper = mountWithIntl( ''} + getFilter={() => null} selectedItems={[mockRule]} onPerformingAction={() => {}} onActionPerformed={() => {}} @@ -152,7 +152,7 @@ describe('rule_quick_edit_buttons', () => { const wrapper = mountWithIntl( ''} + getFilter={() => null} selectedItems={[mockRule]} onPerformingAction={() => {}} onActionPerformed={() => {}} @@ -202,7 +202,7 @@ describe('rule_quick_edit_buttons', () => { const wrapper = mountWithIntl( ''} + getFilter={() => null} selectedItems={[mockRule]} onPerformingAction={() => {}} onActionPerformed={() => {}} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx index c91c461993a5..f3cbae535d77 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/rule_quick_edit_buttons.tsx @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { KueryNode } from '@kbn/es-query'; import React, { useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiIconTip } from '@elastic/eui'; @@ -21,7 +22,7 @@ import { useKibana } from '../../../../common/lib/kibana'; export type ComponentOpts = { selectedItems: RuleTableItem[]; isAllSelected?: boolean; - getFilter: () => string; + getFilter: () => KueryNode | null; onPerformingAction?: () => void; onActionPerformed?: () => void; isSnoozingRules?: boolean; @@ -35,11 +36,11 @@ export type ComponentOpts = { setRulesToUnsnooze: React.Dispatch>; setRulesToSchedule: React.Dispatch>; setRulesToUnschedule: React.Dispatch>; - setRulesToSnoozeFilter: React.Dispatch>; - setRulesToUnsnoozeFilter: React.Dispatch>; - setRulesToScheduleFilter: React.Dispatch>; - setRulesToUnscheduleFilter: React.Dispatch>; - setRulesToUpdateAPIKeyFilter: React.Dispatch>; + setRulesToSnoozeFilter: React.Dispatch>; + setRulesToUnsnoozeFilter: React.Dispatch>; + setRulesToScheduleFilter: React.Dispatch>; + setRulesToUnscheduleFilter: React.Dispatch>; + setRulesToUpdateAPIKeyFilter: React.Dispatch>; } & BulkOperationsComponentOpts; const ButtonWithTooltip = ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_modal.tsx index bbdcfae785c9..95363c5745b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_modal.tsx @@ -7,6 +7,7 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { KueryNode } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { EuiConfirmModal, @@ -30,8 +31,8 @@ import { useKibana } from '../../../../common/lib/kibana'; export type BulkSnoozeModalProps = { rulesToSnooze: RuleTableItem[]; rulesToUnsnooze: RuleTableItem[]; - rulesToSnoozeFilter?: string; - rulesToUnsnoozeFilter?: string; + rulesToSnoozeFilter?: KueryNode | null | undefined; + rulesToUnsnoozeFilter?: KueryNode | null | undefined; numberOfSelectedRules?: number; onClose: () => void; onSave: () => void; @@ -82,14 +83,14 @@ export const BulkSnoozeModal = (props: BulkSnoozeModalProps) => { const { showToast } = useBulkEditResponse({ onSearchPopulate }); const isSnoozeModalOpen = useMemo(() => { - if (rulesToSnoozeFilter) { + if (typeof rulesToSnoozeFilter !== 'undefined') { return true; } return rulesToSnooze.length > 0; }, [rulesToSnooze, rulesToSnoozeFilter]); const isUnsnoozeModalOpen = useMemo(() => { - if (rulesToUnsnoozeFilter) { + if (typeof rulesToUnsnoozeFilter !== 'undefined') { return true; } return rulesToUnsnooze.length > 0; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx index cbf009acaa9a..d5a1fd3d62b7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx @@ -7,6 +7,7 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { KueryNode } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { EuiConfirmModal, @@ -50,8 +51,8 @@ const deleteConfirmSingle = (ruleName: string) => export type BulkSnoozeScheduleModalProps = { rulesToSchedule: RuleTableItem[]; rulesToUnschedule: RuleTableItem[]; - rulesToScheduleFilter?: string; - rulesToUnscheduleFilter?: string; + rulesToScheduleFilter?: KueryNode | null | undefined; + rulesToUnscheduleFilter?: KueryNode | null | undefined; numberOfSelectedRules?: number; onClose: () => void; onSave: () => void; @@ -83,14 +84,14 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) => const { showToast } = useBulkEditResponse({ onSearchPopulate }); const isScheduleModalOpen = useMemo(() => { - if (rulesToScheduleFilter) { + if (typeof rulesToScheduleFilter !== 'undefined') { return true; } return rulesToSchedule.length > 0; }, [rulesToSchedule, rulesToScheduleFilter]); const isUnscheduleModalOpen = useMemo(() => { - if (rulesToUnscheduleFilter) { + if (typeof rulesToUnscheduleFilter !== 'undefined') { return true; } return rulesToUnschedule.length > 0; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 0061e56d4849..a06958fb3072 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { capitalize, isEmpty, sortBy } from 'lodash'; +import { KueryNode } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useEffect, useState, ReactNode, useCallback, useMemo } from 'react'; import { @@ -206,20 +207,31 @@ export const RulesList = ({ const [rulesToDelete, setRulesToDelete] = useState([]); + // TODO - tech debt: Right now we're using null and undefined to determine if we should + // render the bulk edit modal. Refactor this to only keep track of 1 set of rules and types + // to determine which modal to show const [rulesToSnooze, setRulesToSnooze] = useState([]); - const [rulesToSnoozeFilter, setRulesToSnoozeFilter] = useState(''); + const [rulesToSnoozeFilter, setRulesToSnoozeFilter] = useState(); const [rulesToUnsnooze, setRulesToUnsnooze] = useState([]); - const [rulesToUnsnoozeFilter, setRulesToUnsnoozeFilter] = useState(''); + const [rulesToUnsnoozeFilter, setRulesToUnsnoozeFilter] = useState< + KueryNode | null | undefined + >(); const [rulesToSchedule, setRulesToSchedule] = useState([]); - const [rulesToScheduleFilter, setRulesToScheduleFilter] = useState(''); + const [rulesToScheduleFilter, setRulesToScheduleFilter] = useState< + KueryNode | null | undefined + >(); const [rulesToUnschedule, setRulesToUnschedule] = useState([]); - const [rulesToUnscheduleFilter, setRulesToUnscheduleFilter] = useState(''); + const [rulesToUnscheduleFilter, setRulesToUnscheduleFilter] = useState< + KueryNode | null | undefined + >(); const [rulesToUpdateAPIKey, setRulesToUpdateAPIKey] = useState([]); - const [rulesToUpdateAPIKeyFilter, setRulesToUpdateAPIKeyFilter] = useState(''); + const [rulesToUpdateAPIKeyFilter, setRulesToUpdateAPIKeyFilter] = useState< + KueryNode | null | undefined + >(); const [isSnoozingRules, setIsSnoozingRules] = useState(false); const [isSchedulingRules, setIsSchedulingRules] = useState(false); @@ -586,6 +598,12 @@ export const RulesList = ({ } = useBulkEditSelect({ totalItemCount: rulesState.totalItemCount, items: tableItems, + searchText, + typesFilter: rulesTypesFilter, + actionTypesFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + tagsFilter, }); const authorizedToModifySelectedRules = useMemo(() => { @@ -602,27 +620,27 @@ export const RulesList = ({ const clearRulesToSnooze = () => { setRulesToSnooze([]); - setRulesToSnoozeFilter(''); + setRulesToSnoozeFilter(undefined); }; const clearRulesToUnsnooze = () => { setRulesToUnsnooze([]); - setRulesToUnsnoozeFilter(''); + setRulesToUnsnoozeFilter(undefined); }; const clearRulesToSchedule = () => { setRulesToSchedule([]); - setRulesToScheduleFilter(''); + setRulesToScheduleFilter(undefined); }; const clearRulesToUnschedule = () => { setRulesToUnschedule([]); - setRulesToUnscheduleFilter(''); + setRulesToUnscheduleFilter(undefined); }; const clearRulesToUpdateAPIKey = () => { setRulesToUpdateAPIKey([]); - setRulesToUpdateAPIKeyFilter(''); + setRulesToUpdateAPIKeyFilter(undefined); }; const isRulesTableLoading = useMemo(() => { From 411f80d0d8634a7daaa17c47145e0deebe4969be Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 27 Sep 2022 22:39:46 -0600 Subject: [PATCH 046/185] [api-docs] Daily api_docs build (#142030) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.devdocs.json | 56 + api_docs/core.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 4 +- api_docs/data_query.devdocs.json | 54 +- api_docs/data_query.mdx | 4 +- api_docs/data_search.mdx | 4 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 4 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.devdocs.json | 19 +- api_docs/files.mdx | 4 +- api_docs/fleet.devdocs.json | 267 ++- api_docs/fleet.mdx | 4 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- .../kbn_core_injected_metadata_browser.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ...solution_io_ts_alerting_types.devdocs.json | 70 +- ..._securitysolution_io_ts_alerting_types.mdx | 4 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- ..._securitysolution_io_ts_types.devdocs.json | 71 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 4 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.devdocs.json | 48 + api_docs/lens.mdx | 4 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.devdocs.json | 24 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 22 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.devdocs.json | 1524 ++++++++++++++++- api_docs/triggers_actions_ui.mdx | 4 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 411 files changed, 2350 insertions(+), 627 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index c298d8a395df..4d0790ec9518 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: 2022-09-27 +date: 2022-09-28 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 6da505acae8b..88e4157414a7 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 4e5199b31ef9..c3bd7eb349a0 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: 2022-09-27 +date: 2022-09-28 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 df1ba878d7e6..cb7a1008b0bc 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: 2022-09-27 +date: 2022-09-28 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 84c92a1ce864..d9218c6f9c48 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index e10dd6028faf..b574eadcc22f 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: 2022-09-27 +date: 2022-09-28 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 504dc027276f..e8fa3ead871d 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: 2022-09-27 +date: 2022-09-28 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 5dc73b2b9000..9c484f7dfc61 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: 2022-09-27 +date: 2022-09-28 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 aaa279d69844..2ec486a4957e 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: 2022-09-27 +date: 2022-09-28 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 5e129b2343ca..b204ddb7b0ee 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: 2022-09-27 +date: 2022-09-28 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 5c1a4fea8ca5..fdf2f80ec284 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 6314cca4f901..33e642108103 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 9c7112ee6e3a..8cdffb406e1d 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: 2022-09-27 +date: 2022-09-28 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 a673eebc1e1c..e365d6a87afe 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 849d5fc71f96..69b2dfc22a26 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index 08008a2bfe5c..607bae8fad1f 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -584,6 +584,10 @@ "plugin": "@kbn/core-analytics-server-internal", "path": "packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts" }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.ts" + }, { "plugin": "@kbn/core-status-server-internal", "path": "packages/core/status/core-status-server-internal/src/status_service.ts" @@ -620,6 +624,30 @@ "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" @@ -19295,6 +19323,10 @@ "plugin": "@kbn/core-analytics-server-internal", "path": "packages/core/analytics/core-analytics-server-internal/src/analytics_service.ts" }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.ts" + }, { "plugin": "@kbn/core-status-server-internal", "path": "packages/core/status/core-status-server-internal/src/status_service.ts" @@ -19331,6 +19363,30 @@ "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, + { + "plugin": "@kbn/core-root-browser-internal", + "path": "packages/core/root/core-root-browser-internal/src/core_system.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" diff --git a/api_docs/core.mdx b/api_docs/core.mdx index d63230e7ed58..d554613d1ec1 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 7c8ab77b2d5a..b25052db9516 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: 2022-09-27 +date: 2022-09-28 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 507845391c77..609a9e092533 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: 2022-09-27 +date: 2022-09-28 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 ef9680b1325d..ae6e7c69b3a8 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: 2022-09-27 +date: 2022-09-28 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 f849f08374de..b8c91c02caa2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3211 | 33 | 2508 | 23 | +| 3213 | 33 | 2509 | 23 | ## Client diff --git a/api_docs/data_query.devdocs.json b/api_docs/data_query.devdocs.json index 2507050e905f..23849f664136 100644 --- a/api_docs/data_query.devdocs.json +++ b/api_docs/data_query.devdocs.json @@ -3785,10 +3785,10 @@ }, { "parentPluginId": "data", - "id": "def-common.queryStateToExpressionAst", + "id": "def-common.textBasedQueryStateToAstWithValidation", "type": "Function", "tags": [], - "label": "queryStateToExpressionAst", + "label": "textBasedQueryStateToAstWithValidation", "description": [ "\nConverts QueryState to expression AST" ], @@ -3801,15 +3801,15 @@ "section": "def-common.ExpressionAstExpression", "text": "ExpressionAstExpression" }, - ">" + " | undefined>" ], - "path": "src/plugins/data/common/query/to_expression_ast.ts", + "path": "src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "data", - "id": "def-common.queryStateToExpressionAst.$1", + "id": "def-common.textBasedQueryStateToAstWithValidation.$1", "type": "Object", "tags": [], "label": "{\n filters,\n query,\n inputQuery,\n time,\n dataViewsService,\n}", @@ -3817,7 +3817,49 @@ "signature": [ "Args" ], - "path": "src/plugins/data/common/query/to_expression_ast.ts", + "path": "src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-common.textBasedQueryStateToExpressionAst", + "type": "Function", + "tags": [], + "label": "textBasedQueryStateToExpressionAst", + "description": [ + "\nConverts QueryState to expression AST" + ], + "signature": [ + "({\n filters,\n query,\n inputQuery,\n time,\n timeFieldName,\n}: Args) => ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionAstExpression", + "text": "ExpressionAstExpression" + } + ], + "path": "src/plugins/data/common/query/text_based_query_state_to_ast.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.textBasedQueryStateToExpressionAst.$1", + "type": "Object", + "tags": [], + "label": "{\n filters,\n query,\n inputQuery,\n time,\n timeFieldName,\n}", + "description": [], + "signature": [ + "Args" + ], + "path": "src/plugins/data/common/query/text_based_query_state_to_ast.ts", "deprecated": false, "trackAdoption": false, "isRequired": true diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index bb88b05d16a9..b81d02840aba 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3211 | 33 | 2508 | 23 | +| 3213 | 33 | 2509 | 23 | ## Client diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 3cf065a95772..12e4806b7ea2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3211 | 33 | 2508 | 23 | +| 3213 | 33 | 2509 | 23 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 6fca87db82f9..1384a135bfbe 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: 2022-09-27 +date: 2022-09-28 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 9e4d3deac15c..4b95039f7981 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: 2022-09-27 +date: 2022-09-28 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 cf43b6fa9dd9..3608114bd15a 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: 2022-09-27 +date: 2022-09-28 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 a5ea60ca6db8..c6389cc090a7 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: 2022-09-27 +date: 2022-09-28 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 797cd5596c31..69bcfa932c46 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index ec45435a4e87..b658b5b79560 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 430ba5481352..dabf6a7945b9 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 30143657a7b1..643d002885ef 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 95bc923cc9da..ddeb12beb9a7 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: 2022-09-27 +date: 2022-09-28 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 8f51330d8bc6..4a2a4abc7d5e 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: 2022-09-27 +date: 2022-09-28 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 7263ed936315..104fc1d5cde6 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index f5d8586e5194..3203b4c95754 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: 2022-09-27 +date: 2022-09-28 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 326d612509b6..e5e64a70e801 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: 2022-09-27 +date: 2022-09-28 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 e5a8bb47a052..6d488adbda83 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: 2022-09-27 +date: 2022-09-28 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 23ae523bade6..2d3bbbfe7ad8 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index d5982b5a2a86..e6eb098f3c9c 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-ma | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 114 | 3 | 110 | 3 | +| 114 | 3 | 110 | 5 | ## Client diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 705fa73b04c1..54a7269ef532 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 53b40226f0d7..dff00025d47a 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 51a1fd22bc03..ba5e5e742f4c 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: 2022-09-27 +date: 2022-09-28 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 c23d6a727bb3..331af14bbd78 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: 2022-09-27 +date: 2022-09-28 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 1196f0794502..36ea696e5335 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: 2022-09-27 +date: 2022-09-28 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 046b3c31f107..97052c63f747 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: 2022-09-27 +date: 2022-09-28 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 9a3e6bd0dc82..95950d42e447 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: 2022-09-27 +date: 2022-09-28 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 76fd8a4068f1..ee6b2d7e353f 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: 2022-09-27 +date: 2022-09-28 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 337717de0a65..c1edcc43088d 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: 2022-09-27 +date: 2022-09-28 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 32c3971bd090..672e12f360d3 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: 2022-09-27 +date: 2022-09-28 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 ca6a5be21ec3..7fbd70637ed3 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: 2022-09-27 +date: 2022-09-28 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 90854571dc71..09f5c5583463 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: 2022-09-27 +date: 2022-09-28 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 898224f9aba1..8414051020d0 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: 2022-09-27 +date: 2022-09-28 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 f6686201cb13..42b6c0963d95 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index c090283f6974..340a2de8e8e2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index cc0dfccc5915..74c3fd828f44 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: 2022-09-27 +date: 2022-09-28 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 116d58a6218b..7b4880c40f8d 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: 2022-09-27 +date: 2022-09-28 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 dbb19f977cc7..8c4ebd13fe23 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 31c1b78e9438..c0963ced99a2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 358859310c79..7c94fcdac902 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -438,10 +438,10 @@ "tags": [], "label": "getDownloadHref", "description": [ - "\nGet a string for downloading a file that can be passed to a button element's\nhref for download." + "\nGet a string for downloading a file that can be passed to a button element's\nhref for download.\n" ], "signature": [ - "(file: ", + "(args: Pick<", { "pluginId": "files", "scope": "common", @@ -449,7 +449,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - ") => string" + ", \"id\" | \"fileKind\">) => string" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -460,9 +460,12 @@ "id": "def-public.FilesClient.getDownloadHref.$1", "type": "Object", "tags": [], - "label": "file", - "description": [], + "label": "args", + "description": [ + "- get download URL args" + ], "signature": [ + "Pick<", { "pluginId": "files", "scope": "common", @@ -470,7 +473,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "" + ", \"id\" | \"fileKind\">" ], "path": "x-pack/plugins/files/public/types.ts", "deprecated": false, @@ -1129,7 +1132,7 @@ "section": "def-common.FileJSON", "text": "FileJSON" }, - "; }>; upload: (arg: Omit & Readonly<{ selfDestructOnAbort?: boolean | undefined; } & {}> & { body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; }, \"kind\">) => Promise<{ ok: true; size: number; }>; download: (arg: Omit & { kind: string; }, \"kind\">) => Promise; getDownloadHref: (arg: Omit<", + "; }>; upload: (arg: Omit & Readonly<{ selfDestructOnAbort?: boolean | undefined; } & {}> & { body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; }, \"kind\">) => Promise<{ ok: true; size: number; }>; download: (arg: Omit & { kind: string; }, \"kind\">) => Promise; getDownloadHref: (arg: Omit, \"kind\">) => string; share: (arg: Omit & Readonly<{} & { fileId: string; }> & { kind: string; }, \"kind\">) => Promise<", + ", \"id\" | \"fileKind\">, \"kind\">) => string; share: (arg: Omit & Readonly<{} & { fileId: string; }> & { kind: string; }, \"kind\">) => Promise<", { "pluginId": "files", "scope": "common", diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 149bec1a8acb..96ec1e67bf2b 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 263 | 0 | 15 | 2 | +| 263 | 0 | 14 | 2 | ## Client diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index a35508ec3930..d068e061e494 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -1225,6 +1225,146 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtension", + "type": "Interface", + "tags": [], + "label": "PackagePolicyCreateMultiStepExtension", + "description": [ + "Extension point registration contract for Integration Policy Create views in multi-step onboarding" + ], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtension.package", + "type": "string", + "tags": [], + "label": "package", + "description": [], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtension.view", + "type": "string", + "tags": [], + "label": "view", + "description": [], + "signature": [ + "\"package-policy-create-multi-step\"" + ], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtension.Component", + "type": "Function", + "tags": [], + "label": "Component", + "description": [], + "signature": [ + "React.ExoticComponent<(", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", + "text": "PackagePolicyCreateMultiStepExtensionComponentProps" + }, + " & React.RefAttributes>) | (", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", + "text": "PackagePolicyCreateMultiStepExtensionComponentProps" + }, + " & { children?: React.ReactNode; })> & { readonly _result: ", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtensionComponent", + "text": "PackagePolicyCreateMultiStepExtensionComponent" + }, + "; }" + ], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtension.Component.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", + "type": "Interface", + "tags": [], + "label": "PackagePolicyCreateMultiStepExtensionComponentProps", + "description": [], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps.newPolicy", + "type": "Object", + "tags": [], + "label": "newPolicy", + "description": [ + "The integration policy being created" + ], + "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.NewPackagePolicy", + "text": "NewPackagePolicy" + } + ], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-public.PackagePolicyEditExtension", @@ -1925,10 +2065,10 @@ "id": "def-public.UIExtensionsStorage.Unnamed", "type": "IndexSignature", "tags": [], - "label": "[key: string]: Partial>", + "label": "[key: string]: Partial>", "description": [], "signature": [ - "[key: string]: Partial | React.FunctionComponent<", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtensionComponentProps", + "text": "PackagePolicyCreateMultiStepExtensionComponentProps" + }, + ">" + ], + "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-public.PackagePolicyEditExtensionComponent", @@ -2269,6 +2442,14 @@ "docId": "kibFleetPluginApi", "section": "def-public.AgentEnrollmentFlyoutFinalStepExtension", "text": "AgentEnrollmentFlyoutFinalStepExtension" + }, + " | ", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtension", + "text": "PackagePolicyCreateMultiStepExtension" } ], "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", @@ -2365,6 +2546,14 @@ "docId": "kibFleetPluginApi", "section": "def-public.AgentEnrollmentFlyoutFinalStepExtension", "text": "AgentEnrollmentFlyoutFinalStepExtension" + }, + " | ", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtension", + "text": "PackagePolicyCreateMultiStepExtension" } ], "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", @@ -3725,6 +3914,14 @@ "docId": "kibFleetPluginApi", "section": "def-public.AgentEnrollmentFlyoutFinalStepExtension", "text": "AgentEnrollmentFlyoutFinalStepExtension" + }, + " | ", + { + "pluginId": "fleet", + "scope": "public", + "docId": "kibFleetPluginApi", + "section": "def-public.PackagePolicyCreateMultiStepExtension", + "text": "PackagePolicyCreateMultiStepExtension" } ], "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", @@ -6227,7 +6424,7 @@ "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, - " | undefined; force?: boolean | undefined; } | undefined, currentVersion?: string | undefined) => Promise<", + " | undefined; force?: boolean | undefined; skipUniqueNameVerification?: boolean | undefined; } | undefined, currentVersion?: string | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -6352,6 +6549,20 @@ "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.update.$5.skipUniqueNameVerification", + "type": "CompoundType", + "tags": [], + "label": "skipUniqueNameVerification", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false } ] }, @@ -9276,14 +9487,14 @@ { "parentPluginId": "fleet", "id": "def-common.FleetServerAgent.upgraded_at", - "type": "string", + "type": "CompoundType", "tags": [], "label": "upgraded_at", "description": [ "\nDate/time the Elastic Agent was last upgraded" ], "signature": [ - "string | undefined" + "string | null | undefined" ], "path": "x-pack/plugins/fleet/common/types/models/agent.ts", "deprecated": false, @@ -9305,22 +9516,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "fleet", - "id": "def-common.FleetServerAgent.upgrade_status", - "type": "CompoundType", - "tags": [], - "label": "upgrade_status", - "description": [ - "\nUpgrade status" - ], - "signature": [ - "\"completed\" | \"started\" | undefined" - ], - "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "fleet", "id": "def-common.FleetServerAgent.access_api_key_id", @@ -14202,21 +14397,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "fleet", - "id": "def-common.ENDPOINT_PRIVILEGES", - "type": "Array", - "tags": [], - "label": "ENDPOINT_PRIVILEGES", - "description": [], - "signature": [ - "string[]" - ], - "path": "x-pack/plugins/fleet/common/constants/authz.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "fleet", "id": "def-common.EsAssetReference", @@ -16546,6 +16726,21 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-common.ENDPOINT_PRIVILEGES", + "type": "Object", + "tags": [], + "label": "ENDPOINT_PRIVILEGES", + "description": [], + "signature": [ + "readonly [\"writeEndpointList\", \"readEndpointList\", \"writeTrustedApplications\", \"readTrustedApplications\", \"writeHostIsolationExceptions\", \"readHostIsolationExceptions\", \"writeBlocklist\", \"readBlocklist\", \"writeEventFilters\", \"readEventFilters\", \"writePolicyManagement\", \"readPolicyManagement\", \"writeActionsLogManagement\", \"readActionsLogManagement\", \"writeHostIsolation\", \"writeProcessOperations\", \"writeFileOperations\"]" + ], + "path": "x-pack/plugins/fleet/common/constants/authz.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-common.EPM_API_ROUTES", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 173f9f76092f..6f966dbb5a31 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 988 | 3 | 888 | 17 | +| 996 | 3 | 893 | 17 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index c689891e7444..63433541ece2 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: 2022-09-27 +date: 2022-09-28 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 5dfb91864c06..c28c8b2fef87 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: 2022-09-27 +date: 2022-09-28 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 62db97259ee5..8c285e037a35 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 87bd3e83a63b..2d1523f302cb 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: 2022-09-27 +date: 2022-09-28 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 eee8cda64e24..46b6303673ab 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index fd32cbab9919..d86809062de0 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 00175e67b30b..0a75d4c550c4 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 33ee5123bb5c..1b2ce0d5a84c 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index bf08287ddc42..3ca0d95b596d 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index d4cb51aa5139..7e469da7a40b 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 389117150bd5..9e1d8daed9b7 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 749a781cf0e4..294c0b5ebb04 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index c0f7031e47a7..e991378eefb6 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 1595bab913e8..61ec0f495851 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index ba7823ee64ac..af69ccaa6645 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 105ff1b565b7..c80fb067196d 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index ef9a534fa347..c8fd4ef97940 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 6a263b0defbb..102c627e9ef5 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index defb49cbe69e..88ae4fc21aca 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: 2022-09-27 +date: 2022-09-28 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 0a80a8f9d7c6..951b6896fa52 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index f87f80a173be..a679b1a252da 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 465473d3ef7c..c330c66a3b51 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 8bc8181b1c66..4cf5662b020c 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: 2022-09-27 +date: 2022-09-28 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 26ac1ad8da54..4396f3ffe02e 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: 2022-09-27 +date: 2022-09-28 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 79c65aec69db..1857d82ee013 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: 2022-09-27 +date: 2022-09-28 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 4b5645cc4b13..f3779a0488fb 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: 2022-09-27 +date: 2022-09-28 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 fb74c109385e..e2d60d46020a 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 6dfdbdc6a7e1..e0475bc85ecd 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: 2022-09-27 +date: 2022-09-28 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 e5787da85b66..32514d096d20 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: 2022-09-27 +date: 2022-09-28 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 9f30996b585d..b93c70c8a0eb 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: 2022-09-27 +date: 2022-09-28 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 97256b751610..78d758c0f2fe 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 12779749648d..812ca543dea5 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 89fc744b1c70..c29081c8b626 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: 2022-09-27 +date: 2022-09-28 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 bad36ac54b21..739c7e7af596 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: 2022-09-27 +date: 2022-09-28 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 5db786b7754f..2c0999666ff1 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: 2022-09-27 +date: 2022-09-28 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 0141718a2ccd..f58a16ef732a 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: 2022-09-27 +date: 2022-09-28 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 2d21be701e13..ced023377219 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: 2022-09-27 +date: 2022-09-28 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 1dc30aff04e5..cbea4bff1bf8 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: 2022-09-27 +date: 2022-09-28 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 7e999f21f3e1..b1580098a365 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: 2022-09-27 +date: 2022-09-28 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 c957561aa4ec..f704df4a7f34 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: 2022-09-27 +date: 2022-09-28 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 6ba6188770d4..4767d8fa5053 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: 2022-09-27 +date: 2022-09-28 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 09bb2339c978..7c6ce1341617 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: 2022-09-27 +date: 2022-09-28 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 a036a5bb51c0..17402d105d5b 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: 2022-09-27 +date: 2022-09-28 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 42db1a258137..48cb4145874c 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: 2022-09-27 +date: 2022-09-28 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index cd0875458844..28228f4c7476 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: 2022-09-27 +date: 2022-09-28 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 468ebd13bf1b..09afb9dd896c 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: 2022-09-27 +date: 2022-09-28 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 c986f9acf0ef..bf845366f8e6 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: 2022-09-27 +date: 2022-09-28 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 d6cd91c67140..bf1d2e6b2545 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: 2022-09-27 +date: 2022-09-28 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 7a6ba37310aa..a2eb49783d4b 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: 2022-09-27 +date: 2022-09-28 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 24b234f7f811..aa1d950326fc 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: 2022-09-27 +date: 2022-09-28 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 c5359e8345a3..3d8690f8d6f7 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: 2022-09-27 +date: 2022-09-28 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 b847a51cd7a0..78563e898c03 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index cc02f6073ea9..4b44cbfe0757 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: 2022-09-27 +date: 2022-09-28 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 b1d46f4e5fe8..f8052fad772a 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: 2022-09-27 +date: 2022-09-28 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 ee601e6352e3..79da95fae9e6 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: 2022-09-27 +date: 2022-09-28 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 6eec129fe261..ad16ad6aea2e 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: 2022-09-27 +date: 2022-09-28 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 983047e82691..7fe5dba92aa5 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: 2022-09-27 +date: 2022-09-28 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 0190b56cf973..2547deec251f 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index 60d444ced08d..d96ff821c436 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index e9f8e03e21a2..74dee4cd3fdc 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: 2022-09-27 +date: 2022-09-28 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 cd76ecc1e5de..da6a21361ea2 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: 2022-09-27 +date: 2022-09-28 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 adcf635bd089..617a402d35fc 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: 2022-09-27 +date: 2022-09-28 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 d8f11b0a7890..1217a7b8fa20 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: 2022-09-27 +date: 2022-09-28 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 04891d7eee77..0ccaad98b392 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: 2022-09-27 +date: 2022-09-28 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 a211477391f8..14eccd7c10c3 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: 2022-09-27 +date: 2022-09-28 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 cabbd8bcaf86..f5f017918ee4 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: 2022-09-27 +date: 2022-09-28 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 c6a7f734219d..477917148b69 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: 2022-09-27 +date: 2022-09-28 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 7af48bcbf5f1..399790680e06 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: 2022-09-27 +date: 2022-09-28 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 ca2a47bc9933..318e2f19b36d 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: 2022-09-27 +date: 2022-09-28 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 5c6a4b84856c..b4e402b1d85a 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: 2022-09-27 +date: 2022-09-28 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 dd0e97e30fc7..1263eb43812b 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: 2022-09-27 +date: 2022-09-28 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 2d4f36ed093a..efd3c4dce7d8 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: 2022-09-27 +date: 2022-09-28 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 587243bb6406..4b8b92e29d47 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: 2022-09-27 +date: 2022-09-28 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 96b79793ed09..12f2b1b89aaa 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: 2022-09-27 +date: 2022-09-28 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 d8cfe4d9f349..22703ff1d56c 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: 2022-09-27 +date: 2022-09-28 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 fe90efd44105..58d7084730b0 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: 2022-09-27 +date: 2022-09-28 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 e7b47436fa76..9f241f79f309 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: 2022-09-27 +date: 2022-09-28 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 8fa86908f719..2d3ce9691dbc 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: 2022-09-27 +date: 2022-09-28 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 76ca7ae6c111..db3e364e96ff 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: 2022-09-27 +date: 2022-09-28 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 3e17e9ed9463..cbeb87f2524b 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: 2022-09-27 +date: 2022-09-28 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 01d683396e2b..e838dfc832fd 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: 2022-09-27 +date: 2022-09-28 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 891c8d876754..6bdcb4cd9e37 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: 2022-09-27 +date: 2022-09-28 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index a935d019adeb..afb5974de9a1 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: 2022-09-27 +date: 2022-09-28 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 04f71c530c9a..8e28ebbe2ff1 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: 2022-09-27 +date: 2022-09-28 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 53f1d9fe670a..463b56f36429 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: 2022-09-27 +date: 2022-09-28 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 f8ff0087dbbf..bf4cf64d7a58 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: 2022-09-27 +date: 2022-09-28 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 c93bb2546524..4047f8a6f484 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: 2022-09-27 +date: 2022-09-28 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 6121b09f8c1b..650426f49357 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 49dffda39dcc..0df0a232ed7e 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_http_server.mdx index 74c3216b75eb..404f13d3ca12 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 50ebcbe4058b..524b236008cf 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index c7649dcf597c..94a0469ff107 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: 2022-09-27 +date: 2022-09-28 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 7e8106d7e3c6..aac9e17df421 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: 2022-09-27 +date: 2022-09-28 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 32e6b9184dbe..e83f6e8553d8 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: 2022-09-27 +date: 2022-09-28 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 4e5cbf65974a..a89d07338f78 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: 2022-09-27 +date: 2022-09-28 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 93c620cca65a..3575cc0d18a9 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: 2022-09-27 +date: 2022-09-28 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 13734215059b..ce24eb49860c 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 08dbe296b432..92e9d0e07cfe 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 b88675d50c20..17e4abfa2a1a 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: 2022-09-27 +date: 2022-09-28 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 fc53dc77eda1..d8c9f151e2f4 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: 2022-09-27 +date: 2022-09-28 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 2526e6dc2b6b..574ff5014691 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: 2022-09-27 +date: 2022-09-28 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 8b557e2e502b..62c8d4f2358e 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: 2022-09-27 +date: 2022-09-28 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 74231286ffe0..8deee157e3d4 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: 2022-09-27 +date: 2022-09-28 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index a4e8cb4241be..cf1126fe73e2 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: 2022-09-27 +date: 2022-09-28 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 f56eecbd6955..bc4d09292edb 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: 2022-09-27 +date: 2022-09-28 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 2067fdbc72fd..485e124d97e4 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: 2022-09-27 +date: 2022-09-28 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 a7e7a11ae05e..4b17dbaac8c8 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: 2022-09-27 +date: 2022-09-28 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 febc5d303893..36c1e8c0f9a4 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: 2022-09-27 +date: 2022-09-28 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 47cc583e2df0..a1a8ce47aed5 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: 2022-09-27 +date: 2022-09-28 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 9e16d8304dec..6b4a55f82676 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: 2022-09-27 +date: 2022-09-28 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 5db83c7383b6..d1494db23637 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: 2022-09-27 +date: 2022-09-28 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 8fafac583163..ad35d06977cc 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: 2022-09-27 +date: 2022-09-28 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 9fbffa9c5df9..5b5b8a942141 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: 2022-09-27 +date: 2022-09-28 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 d253278b97f1..bf95da355c9e 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: 2022-09-27 +date: 2022-09-28 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 5dd3d790c3ea..3a07f7a67606 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: 2022-09-27 +date: 2022-09-28 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 ed0c7c4e240c..130bddee31d1 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: 2022-09-27 +date: 2022-09-28 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 5c44022f6082..fef5e6c2f9a8 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: 2022-09-27 +date: 2022-09-28 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 ef4b9a440b59..724251e5e502 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: 2022-09-27 +date: 2022-09-28 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 559df72a076e..3837e623f29c 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: 2022-09-27 +date: 2022-09-28 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 6160c98032fe..0f7f597d06d3 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: 2022-09-27 +date: 2022-09-28 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 5765d85478d6..42846c64f33d 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: 2022-09-27 +date: 2022-09-28 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 09d09fe70857..a52c3697e57d 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: 2022-09-27 +date: 2022-09-28 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 5416044cac95..238252f104b3 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: 2022-09-27 +date: 2022-09-28 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 962d718e0042..d5b97eb1a0cb 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: 2022-09-27 +date: 2022-09-28 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 07d55b8bdc9a..20a753b66ea9 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: 2022-09-27 +date: 2022-09-28 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 f31451fde9e7..c074d0f80cb8 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: 2022-09-27 +date: 2022-09-28 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 97d1f20eba7a..949af62592ec 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: 2022-09-27 +date: 2022-09-28 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 1c1ec6407d24..6fa09c36e31f 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: 2022-09-27 +date: 2022-09-28 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 678cf37d24e8..7c588cdc511e 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 da93d1e0b855..447e323f6f6c 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: 2022-09-27 +date: 2022-09-28 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 73d6c70c1a9a..9931b55fa4f2 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: 2022-09-27 +date: 2022-09-28 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 2b499a47657b..deb24928a107 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: 2022-09-27 +date: 2022-09-28 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 fcec7c16e5f8..3bfa422bdff9 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: 2022-09-27 +date: 2022-09-28 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 197152a6dae0..1a306a18a3eb 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: 2022-09-27 +date: 2022-09-28 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 80320e731248..a34e632cf249 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: 2022-09-27 +date: 2022-09-28 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 63fa1e47ac1b..59d4d1841c7f 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: 2022-09-27 +date: 2022-09-28 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 127679f75147..f0610e31ec80 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: 2022-09-27 +date: 2022-09-28 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 f633f52c6174..cd9ba3c71908 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: 2022-09-27 +date: 2022-09-28 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 bc446d004b24..f2dd4437d43e 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: 2022-09-27 +date: 2022-09-28 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 ecbb1447d1e5..88c329e9ae34 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: 2022-09-27 +date: 2022-09-28 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 c694b3c9f658..df7eaa590f6d 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: 2022-09-27 +date: 2022-09-28 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 39099151043a..5534b4652be5 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: 2022-09-27 +date: 2022-09-28 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 73e0b0550438..ccf532e33ba3 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: 2022-09-27 +date: 2022-09-28 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 0012db4e553f..5b78e21ba58c 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: 2022-09-27 +date: 2022-09-28 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 1ef8c6abfc97..f4f9cc899056 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: 2022-09-27 +date: 2022-09-28 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 7c9440c92271..86d04a0b4ff7 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: 2022-09-27 +date: 2022-09-28 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 bd5556b6ce87..52e756da1a55 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: 2022-09-27 +date: 2022-09-28 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 364b20ab80d6..432a7d95007e 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: 2022-09-27 +date: 2022-09-28 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 aa7990a556b5..8ccca542c4b7 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: 2022-09-27 +date: 2022-09-28 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 d4a0a789572f..ab86530abfbb 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: 2022-09-27 +date: 2022-09-28 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 826ee5d1b345..9de9031c75db 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: 2022-09-27 +date: 2022-09-28 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index e68a43d0ce9f..9c59e5a04b8e 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: 2022-09-27 +date: 2022-09-28 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 8c938db4082e..02d479962f92 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 0cbb8955ac42..71eada8e5603 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: 2022-09-27 +date: 2022-09-28 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 67a42c6099d9..b8b744e259c5 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: 2022-09-27 +date: 2022-09-28 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 791006e8cea6..a89cdf92e9ac 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: 2022-09-27 +date: 2022-09-28 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 b1ae46812b1d..290774ac1f9e 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: 2022-09-27 +date: 2022-09-28 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 ef778256b799..004e65223547 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: 2022-09-27 +date: 2022-09-28 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 545b597ef2b8..84190ae50348 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: 2022-09-27 +date: 2022-09-28 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 99402f8c657c..2f362f1ede3f 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: 2022-09-27 +date: 2022-09-28 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 a5081a8a512e..e214233f02eb 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index b490e18b9988..b8ac7249cb2a 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 01b13b900344..4f5cca38779c 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: 2022-09-27 +date: 2022-09-28 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 90ea1b666c8a..bc10003b1203 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: 2022-09-27 +date: 2022-09-28 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_crypto.mdx b/api_docs/kbn_crypto.mdx index a9fb17b3cd06..d95c8ad7295e 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: 2022-09-27 +date: 2022-09-28 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 0afac1bca8d3..06ce6dd99935 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index d2e455811513..4bcac88980eb 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 42e02f978350..42846a27d0d5 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: 2022-09-27 +date: 2022-09-28 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 5169411f49a2..5db4302dd4cd 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: 2022-09-27 +date: 2022-09-28 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 5872d911b8f4..968955efd32a 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: 2022-09-27 +date: 2022-09-28 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 8f8299fb3972..e047906625c2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 93faea017a0f..c7d4d0fe3d26 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: 2022-09-27 +date: 2022-09-28 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 f29fe40eb21d..30e9b543d9d8 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 2d7b26292e0e..8590060b0796 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index da7db13bb38b..1e69af86cfda 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: 2022-09-27 +date: 2022-09-28 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 3d7f8993dd9f..695ed989dfe9 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: 2022-09-27 +date: 2022-09-28 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 db9354454f22..b329278d8272 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: 2022-09-27 +date: 2022-09-28 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 5bee2fdbf5f6..9a2cfbe08a28 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: 2022-09-27 +date: 2022-09-28 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 f6c2a308c267..2e0b9742678d 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 52dcd1a4217e..0373e71bbf94 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index e8d91e141f19..f0a54f4a6740 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: 2022-09-27 +date: 2022-09-28 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 93882b49ca42..a2bfedd08171 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: 2022-09-27 +date: 2022-09-28 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_generate.mdx b/api_docs/kbn_generate.mdx index 5955c7f01158..2a9f62b4f3f3 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index e50741bdb3a6..b1b28470de32 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 24eedfb0387a..b675d4d8f4da 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: 2022-09-27 +date: 2022-09-28 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 1d4fddcc0225..b7abe8b51295 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 59d37e8a5d09..e7a5d6fe0f7e 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: 2022-09-27 +date: 2022-09-28 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 5e218789e57f..f716a25e4408 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: 2022-09-27 +date: 2022-09-28 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 15647319a90f..25ba31dee6a6 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 56039851af76..c36a372e85d7 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 807a82e9cbff..6051ff9b96e1 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index dfb4aea12e2d..0554879f31ad 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 28487fab7140..c005d1ea66f6 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: 2022-09-27 +date: 2022-09-28 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 01244be181bf..b2cd34e94a50 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 7d70916d448e..1b91d601c224 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 63d9dc96362a..13444331d9fc 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: 2022-09-27 +date: 2022-09-28 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 6b1049cee9be..d6db9caa5dd0 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index a175904d0257..12ab660fc176 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index ca5e2566d884..a0fbd8c5ef3c 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 78c0d9fbb836..6808dca6dbe3 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: 2022-09-27 +date: 2022-09-28 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index ffdc48a0fb0d..8da4778d8b81 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: 2022-09-27 +date: 2022-09-28 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index a1f168d3fc91..e9cf6c1838c5 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 7da86649ddf3..4ec4c4ef7c9e 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 29b3596a8bc2..757c4018832a 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: 2022-09-27 +date: 2022-09-28 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 fb729fc0747e..11961ef3d602 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: 2022-09-27 +date: 2022-09-28 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 54d57200bd5a..a980b80bd7a3 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: 2022-09-27 +date: 2022-09-28 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index ba91201a52c6..b1051ed927c8 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: 2022-09-27 +date: 2022-09-28 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 493ca235ab7a..98d5196dc24f 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: 2022-09-27 +date: 2022-09-28 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 b16b234ee0f5..a04f5630c37b 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index f7d3bada225b..af33c58333e7 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 65a1a0d424f1..2a373248eac9 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index ef453ad073e4..61d440950cbf 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index f441cc10aa50..4ca4605d2eb9 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index c81350b38632..99858a653681 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: 2022-09-27 +date: 2022-09-28 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_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 8e6bdc15f980..c74046c4b1fa 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: 2022-09-27 +date: 2022-09-28 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.devdocs.json b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json index b42cebbc4fb8..f9869d88c91c 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json @@ -877,21 +877,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", - "id": "def-common.ThrottleOrUndefinedOrNull", - "type": "Type", - "tags": [], - "label": "ThrottleOrUndefinedOrNull", - "description": [], - "signature": [ - "string | null | undefined" - ], - "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.Type", @@ -1383,24 +1368,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", - "id": "def-common.DefaultThrottleNull", - "type": "Object", - "tags": [], - "label": "DefaultThrottleNull", - "description": [ - "\nTypes the DefaultThrottleNull as:\n - If null or undefined, then a null will be set" - ], - "signature": [ - "Type", - "" - ], - "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.DefaultToString", @@ -2921,7 +2888,14 @@ "label": "throttle", "description": [], "signature": [ - "StringC" + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts", "deprecated": false, @@ -2938,31 +2912,15 @@ "signature": [ "UnionC", "<[", - "StringC", - ", ", - "NullC", - "]>" - ], - "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", - "id": "def-common.throttleOrNullOrUndefined", - "type": "Object", - "tags": [], - "label": "throttleOrNullOrUndefined", - "description": [], - "signature": [ "UnionC", "<[", - "StringC", - ", ", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", "NullC", - ", ", - "UndefinedC", "]>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts", diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 4066f50c3352..5f200ca33cd5 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 148 | 0 | 129 | 0 | +| 145 | 0 | 127 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 720ede9fa9e8..75beb67b94d0 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: 2022-09-27 +date: 2022-09-28 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.devdocs.json b/api_docs/kbn_securitysolution_io_ts_types.devdocs.json index 2ec94e9e8c1c..ef934492d28e 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_types.devdocs.json @@ -405,6 +405,41 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-types", + "id": "def-common.TimeDuration", + "type": "Function", + "tags": [], + "label": "TimeDuration", + "description": [], + "signature": [ + "({ allowedUnits, allowedDurations }: TimeDurationType) => ", + "Type", + "" + ], + "path": "packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-types", + "id": "def-common.TimeDuration.$1", + "type": "CompoundType", + "tags": [], + "label": "{ allowedUnits, allowedDurations }", + "description": [], + "signature": [ + "TimeDurationType" + ], + "path": "packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], @@ -644,12 +679,30 @@ "label": "TimeDurationC", "description": [], "signature": [ + "({ allowedUnits, allowedDurations }: TimeDurationType) => ", "Type", "" ], "path": "packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts", "deprecated": false, "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-types", + "id": "def-common.TimeDurationC.$1", + "type": "CompoundType", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "TimeDurationWithAllowedDurations | TimeDurationWithAllowedUnits" + ], + "path": "packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], "initialIsOpen": false }, { @@ -1042,24 +1095,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-io-ts-types", - "id": "def-common.TimeDuration", - "type": "Object", - "tags": [], - "label": "TimeDuration", - "description": [ - "\nTypes the TimeDuration as:\n - A string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time\n - in the format {safe_integer}{timeUnit}, e.g. \"30s\", \"1m\", \"2h\"" - ], - "signature": [ - "Type", - "" - ], - "path": "packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-io-ts-types", "id": "def-common.UUID", diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index f2220252df9f..2525d36a8393 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 63 | 0 | 33 | 0 | +| 65 | 0 | 36 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 4ddd805becee..1729eb1e3192 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: 2022-09-27 +date: 2022-09-28 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 837b102f35b7..e7ea35711a93 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: 2022-09-27 +date: 2022-09-28 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 5e117e7fdd5f..061d996305ac 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: 2022-09-27 +date: 2022-09-28 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 a0d87f4a94bb..19a2ed45a48b 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: 2022-09-27 +date: 2022-09-28 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 a42cbdfe8731..247718cc1d2a 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: 2022-09-27 +date: 2022-09-28 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 c978507107c7..53f33ec3d564 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: 2022-09-27 +date: 2022-09-28 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 d7cd694497c7..0be9d3e0886f 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: 2022-09-27 +date: 2022-09-28 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 c6c4f053e952..b3d353cc6987 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: 2022-09-27 +date: 2022-09-28 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 fc18c7757d1d..aefc65e7066a 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: 2022-09-27 +date: 2022-09-28 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 2c9d51b46886..25cf89691a72 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 1a40ade046a8..aaf7c6592481 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: 2022-09-27 +date: 2022-09-28 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 6d9064ab9d9a..b12d28f86530 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index e28b92febd73..4fafd2467103 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 3515b5ee35bf..401b8763ce31 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: 2022-09-27 +date: 2022-09-28 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 ba2f64392e72..25b21295a4eb 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: 2022-09-27 +date: 2022-09-28 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 6942dcbd38fc..5867910e33d2 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: 2022-09-27 +date: 2022-09-28 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 69a6794fbccd..757324760d8e 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: 2022-09-27 +date: 2022-09-28 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index a8465df7126e..19d3fcf3f0ac 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: 2022-09-27 +date: 2022-09-28 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 9e930a85deb8..f75c2846a047 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: 2022-09-27 +date: 2022-09-28 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 d96c9079cf5f..89d4d761d558 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: 2022-09-27 +date: 2022-09-28 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 86595fb7cfb1..2056f2314bce 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: 2022-09-27 +date: 2022-09-28 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 913279665176..495d91eac254 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: 2022-09-27 +date: 2022-09-28 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 e5b6a7845bbe..7767a12066d2 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: 2022-09-27 +date: 2022-09-28 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 09f6ed5cf1fa..e0d65073b737 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: 2022-09-27 +date: 2022-09-28 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 ef0e7d2b60bf..8c798dd34a9a 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: 2022-09-27 +date: 2022-09-28 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 43ef0de76bb2..243de44838e1 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: 2022-09-27 +date: 2022-09-28 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 eecbe82f148c..38664c4b62fd 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: 2022-09-27 +date: 2022-09-28 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 001790b9086e..3f237d268f07 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: 2022-09-27 +date: 2022-09-28 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 42efbc3a489c..3ff43ae1b626 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: 2022-09-27 +date: 2022-09-28 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 71404d9e00a6..48e497a99004 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: 2022-09-27 +date: 2022-09-28 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index a24b3d7d985a..15f626088ad2 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: 2022-09-27 +date: 2022-09-28 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 b833764dfacb..2e0d73cc4362 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: 2022-09-27 +date: 2022-09-28 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 bf5525e9d8ea..e9accf72f5a0 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: 2022-09-27 +date: 2022-09-28 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 16c867044187..6309733dc1ba 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: 2022-09-27 +date: 2022-09-28 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index ef2f54962c58..c770dd867195 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 8fa73d4c68da..4b531126964e 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: 2022-09-27 +date: 2022-09-28 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 8a049d4872b7..8073cadd73d8 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index f1ba970a4804..c05c4d734c49 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: 2022-09-27 +date: 2022-09-28 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 2ff62c2d04f3..660e36ef4374 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: 2022-09-27 +date: 2022-09-28 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 b77c7bfc44e9..93828baf0544 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index f81111a50279..fc2ed806bd5a 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: 2022-09-27 +date: 2022-09-28 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 eaf2d94c9a05..fe8a35b28280 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 96f5d4d6ab2a..b072b928a5fe 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: 2022-09-27 +date: 2022-09-28 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 db3273ea8883..2cc7dbb077e4 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index fe4e9cade439..4c380a6aada5 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 9744d7e40941..4658f66ec9f0 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 5bf8d7c64175..5c785be7541a 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 24144f46636b..058bbf15a853 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: 2022-09-27 +date: 2022-09-28 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index fe7f2f02b819..ffad848d71c2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 7855bb68d62d..b7a7e0d49898 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: 2022-09-27 +date: 2022-09-28 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 5adb3b961a12..9c4fac9fc222 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: 2022-09-27 +date: 2022-09-28 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 879d594892e8..a08a1f8c35b2 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: 2022-09-27 +date: 2022-09-28 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 13a63d5a655f..484f22d4d3fa 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 17c5bc67927e..86360f220dc2 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 661dda80a022..db2dd293c3e4 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: 2022-09-27 +date: 2022-09-28 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 3a54ee39d711..a8b6bbd60563 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: 2022-09-27 +date: 2022-09-28 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 62af983d7933..e8b72f7cee2d 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: 2022-09-27 +date: 2022-09-28 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 c101218ee3f4..fc9a2e294a61 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: 2022-09-27 +date: 2022-09-28 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 4fc56c34842e..4d4561547ed2 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -1527,6 +1527,24 @@ "children": [], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.DatasourcePublicAPI.isTextBasedLanguage", + "type": "Function", + "tags": [], + "label": "isTextBasedLanguage", + "description": [ + "\nReturns true if this is a text based language datasource" + ], + "signature": [ + "() => boolean" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.DatasourcePublicAPI.getFilters", @@ -11347,6 +11365,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "lens", + "id": "def-common.ENABLE_SQL", + "type": "string", + "tags": [], + "label": "ENABLE_SQL", + "description": [], + "signature": [ + "\"discover:enableSql\"" + ], + "path": "x-pack/plugins/lens/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "lens", "id": "def-common.FormatFactory", @@ -11495,6 +11528,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "lens", + "id": "def-common.OriginalColumn", + "type": "Type", + "tags": [], + "label": "OriginalColumn", + "description": [], + "signature": [ + "{ id: string; label: string; } & ({ operationType: \"date_histogram\"; sourceField: string; } | { operationType: string; sourceField: never; })" + ], + "path": "x-pack/plugins/lens/common/expressions/map_to_columns/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "lens", "id": "def-common.PieChartType", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index e6eb90ca75ba..75d2cd54a2d8 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 646 | 0 | 558 | 42 | +| 649 | 0 | 560 | 42 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 745b2e56c1c9..42f4d0805fc3 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: 2022-09-27 +date: 2022-09-28 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 3aa129254bb5..ca3e9e1a23c1 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: 2022-09-27 +date: 2022-09-28 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 f4406dfe719d..67cd87e28a5c 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 6aa4f4f22519..c0fba6cbabbb 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 92b5f7e80678..7a332ae0f68b 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: 2022-09-27 +date: 2022-09-28 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 2e5da06f70dd..c821d5118393 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: 2022-09-27 +date: 2022-09-28 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 a2dd183ea48b..30e6fc5864b6 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 55b3f0eff7da..83e81edff74a 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index dbf1d15ca06d..8b27dbc3d784 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: 2022-09-27 +date: 2022-09-28 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 16eb11d15bbf..d0c0e4b93012 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index e9f3d869a207..c8d4e9b1fbf7 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 95e0ce94137e..79f080105b7b 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 7d399eaa890b..d4d3483ef247 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -3704,11 +3704,29 @@ "description": [], "signature": [ "{ get: (id: string) => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "; list: () => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "[]; register: (objectType: ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/observability/public/plugin.ts", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index afdb6cd4fbb3..4c2ca8b17a13 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index a634c2e2206c..942900853abd 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 00a92aa5b813..bcddc85faf5c 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: 2022-09-27 +date: 2022-09-28 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 | |--------------|----------|------------------------| -| 473 | 395 | 38 | +| 474 | 395 | 38 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 31772 | 179 | 21354 | 1002 | +| 31860 | 179 | 21432 | 1007 | ## Plugin Directory @@ -47,7 +47,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 103 | 0 | 84 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 144 | 0 | 139 | 10 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 52 | 0 | 51 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3211 | 33 | 2508 | 23 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3213 | 33 | 2509 | 23 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 60 | 0 | 30 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 0 | @@ -60,7 +60,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 42 | 0 | | | [Enterprise Search](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 9 | 0 | 9 | 0 | -| | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 114 | 3 | 110 | 3 | +| | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 114 | 3 | 110 | 5 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | The Event Annotation service contains expressions for event annotations | 174 | 0 | 174 | 3 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 106 | 0 | 106 | 10 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | @@ -80,8 +80,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 222 | 0 | 95 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 5 | 249 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | 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. | 62 | 0 | 62 | 2 | -| | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 263 | 0 | 15 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 988 | 3 | 888 | 17 | +| | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 263 | 0 | 14 | 2 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 996 | 3 | 893 | 17 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -101,7 +101,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 418 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 646 | 0 | 558 | 42 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 649 | 0 | 560 | 42 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -152,7 +152,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 452 | 1 | 346 | 33 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 437 | 1 | 416 | 45 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 513 | 1 | 486 | 48 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 132 | 0 | 91 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 206 | 0 | 142 | 9 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 61 | 0 | 59 | 2 | @@ -389,9 +389,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | Security Solution auto complete | 56 | 1 | 41 | 1 | | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | | | [Owner missing] | Security Solution utilities for React hooks | 15 | 0 | 7 | 0 | -| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 148 | 0 | 129 | 0 | +| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 145 | 0 | 127 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 505 | 1 | 492 | 0 | -| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 63 | 0 | 33 | 0 | +| | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 65 | 0 | 36 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 28 | 0 | 21 | 0 | | | [Owner missing] | security solution list REST API | 67 | 0 | 64 | 0 | | | [Owner missing] | security solution list constants to use across plugins such lists, security_solution, cases, etc... | 33 | 0 | 17 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 971ed39aee0e..191e4e563cb3 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: 2022-09-27 +date: 2022-09-28 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 8d2be8c0f9dd..fb048ac82442 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 916ffb9e1e4e..c0b505ed6394 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: 2022-09-27 +date: 2022-09-28 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 aa8b69de4519..4088e39f9710 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: 2022-09-27 +date: 2022-09-28 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 7ecab5c862bf..4f8cf9c8e8db 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: 2022-09-27 +date: 2022-09-28 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 42e1a51c2ed9..15c672f3f6fe 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: 2022-09-27 +date: 2022-09-28 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 4c4a4a4be8d7..001d245e9adf 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: 2022-09-27 +date: 2022-09-28 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 d180b9df678f..51bc9b36316c 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: 2022-09-27 +date: 2022-09-28 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 d8ee1bd800f9..3a6b9b0a9e73 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: 2022-09-27 +date: 2022-09-28 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 a57ba63fec00..8fb7925eb780 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: 2022-09-27 +date: 2022-09-28 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 785d7f8ea6a3..9a0469a70f64 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: 2022-09-27 +date: 2022-09-28 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 bf65a9aa5c64..8abc78f1fdc7 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: 2022-09-27 +date: 2022-09-28 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 5e294d8d6ea5..a39c3adbc820 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: 2022-09-27 +date: 2022-09-28 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 4c436c7b23e6..6eaf962f670f 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: 2022-09-27 +date: 2022-09-28 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 5bc1fcd05800..8cb0692d0c85 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 952c20eac620..e6f290046222 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: 2022-09-27 +date: 2022-09-28 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 9cf5459a07a3..c59c106fce25 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 23f8e18b5e27..ad4253bd0d7e 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: 2022-09-27 +date: 2022-09-28 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 7ba452349a66..e6f9de45dbb5 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 194b818f081b..8a45ec4089ad 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: 2022-09-27 +date: 2022-09-28 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 95118ea8b805..94f882172834 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: 2022-09-27 +date: 2022-09-28 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 b3467e6a698d..cb3df93effb0 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: 2022-09-27 +date: 2022-09-28 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 aabbd1805b61..a71b61831fb6 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: 2022-09-27 +date: 2022-09-28 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 d35966ac57c9..b84502a5d8c5 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: 2022-09-27 +date: 2022-09-28 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 1dbef1ee43f3..dfdbd20746f5 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: 2022-09-27 +date: 2022-09-28 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 438e8d307b1d..edb998b57d6f 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: 2022-09-27 +date: 2022-09-28 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 f032f6eee5e0..c23dd99eb95e 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: 2022-09-27 +date: 2022-09-28 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 0b1523b1a10d..5e8ae624dfe8 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: 2022-09-27 +date: 2022-09-28 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 b584556618af..80ac38a8ed99 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: 2022-09-27 +date: 2022-09-28 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 14ef93c594cc..ecb0a115ea51 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: 2022-09-27 +date: 2022-09-28 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 b66dc03b52ac..70b733e2c869 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 59be983bb00f..316d5941b9d2 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -303,6 +303,38 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ButtonGroupField", + "type": "Function", + "tags": [], + "label": "ButtonGroupField", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/button_group_field.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ButtonGroupField.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.CreateConnectorFlyout", @@ -832,6 +864,145 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.hasMustacheTokens", + "type": "Function", + "tags": [], + "label": "hasMustacheTokens", + "description": [], + "signature": [ + "(str: string) => boolean" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.hasMustacheTokens.$1", + "type": "string", + "tags": [], + "label": "str", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.HiddenField", + "type": "Function", + "tags": [], + "label": "HiddenField", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + "Props", + "> & { readonly type: (props: ", + "Props", + ") => JSX.Element; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/hidden_field.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.HiddenField.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.JsonEditorWithMessageVariables", + "type": "Function", + "tags": [], + "label": "JsonEditorWithMessageVariables", + "description": [], + "signature": [ + "({ buttonTitle, messageVariables, paramsProperty, inputTargetValue, label, errors, areaLabel, onDocumentsChange, helpText, onBlur, showButtonTitle, euiCodeEditorProps, }: React.PropsWithChildren) => JSX.Element" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.JsonEditorWithMessageVariables.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n buttonTitle,\n messageVariables,\n paramsProperty,\n inputTargetValue,\n label,\n errors,\n areaLabel,\n onDocumentsChange,\n helpText,\n onBlur,\n showButtonTitle,\n euiCodeEditorProps = {},\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.JsonFieldWrapper", + "type": "Function", + "tags": [], + "label": "JsonFieldWrapper", + "description": [], + "signature": [ + "({ field, ...rest }: Props) => JSX.Element" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/json_field_wrapper.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.JsonFieldWrapper.$1", + "type": "Object", + "tags": [], + "label": "{ field, ...rest }", + "description": [], + "signature": [ + "Props" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/json_field_wrapper.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.loadActionErrorLog", @@ -1398,6 +1569,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.MustacheTextFieldWrapper", + "type": "Function", + "tags": [], + "label": "MustacheTextFieldWrapper", + "description": [], + "signature": [ + "({ field, euiFieldProps, idAria, ...rest }: Props) => JSX.Element" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/mustache_text_field_wrapper.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.MustacheTextFieldWrapper.$1", + "type": "Object", + "tags": [], + "label": "{ field, euiFieldProps, idAria, ...rest }", + "description": [], + "signature": [ + "Props" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/mustache_text_field_wrapper.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.muteRule", @@ -1490,6 +1694,70 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.PasswordField", + "type": "Function", + "tags": [], + "label": "PasswordField", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/password_field.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.PasswordField.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.SimpleConnectorForm", + "type": "Function", + "tags": [], + "label": "SimpleConnectorForm", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.SimpleConnectorForm.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.snoozeRule", @@ -1614,72 +1882,185 @@ }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ThresholdExpression", + "id": "def-public.templateActionVariable", "type": "Function", "tags": [], - "label": "ThresholdExpression", + "label": "templateActionVariable", "description": [], "signature": [ - "(props: ", - "ThresholdExpressionProps", - ") => JSX.Element" + "(variable: ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.ActionVariable", + "text": "ActionVariable" + }, + ") => string" ], - "path": "x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/template_action_variable.ts", "deprecated": false, "trackAdoption": false, - "returnComment": [], "children": [ { "parentPluginId": "triggersActionsUi", - "id": "def-public.ThresholdExpression.$1", - "type": "Uncategorized", + "id": "def-public.templateActionVariable.$1", + "type": "Object", "tags": [], - "label": "props", + "label": "variable", "description": [], "signature": [ - "T" + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.ActionVariable", + "text": "ActionVariable" + } ], - "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx", + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/template_action_variable.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.unmuteRule", + "id": "def-public.TextAreaWithMessageVariables", "type": "Function", "tags": [], - "label": "unmuteRule", + "label": "TextAreaWithMessageVariables", "description": [], "signature": [ - "({ id, http }: { id: string; http: ", - "HttpSetup", - "; }) => Promise" + "({ messageVariables, paramsProperty, index, inputTargetValue, isDisabled, editAction, label, errors, }: React.PropsWithChildren) => JSX.Element" ], - "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unmute.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "triggersActionsUi", - "id": "def-public.unmuteRule.$1", - "type": "Object", + "id": "def-public.TextAreaWithMessageVariables.$1", + "type": "CompoundType", "tags": [], - "label": "{ id, http }", + "label": "{\n messageVariables,\n paramsProperty,\n index,\n inputTargetValue,\n isDisabled = false,\n editAction,\n label,\n errors,\n}", "description": [], - "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unmute.ts", + "signature": [ + "React.PropsWithChildren" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "triggersActionsUi", - "id": "def-public.unmuteRule.$1.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TextFieldWithMessageVariables", + "type": "Function", + "tags": [], + "label": "TextFieldWithMessageVariables", + "description": [], + "signature": [ + "({ buttonTitle, messageVariables, paramsProperty, index, inputTargetValue, editAction, errors, formRowProps, defaultValue, wrapField, showButtonTitle, }: React.PropsWithChildren) => JSX.Element" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TextFieldWithMessageVariables.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n buttonTitle,\n messageVariables,\n paramsProperty,\n index,\n inputTargetValue,\n editAction,\n errors,\n formRowProps,\n defaultValue,\n wrapField = false,\n showButtonTitle,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ThresholdExpression", + "type": "Function", + "tags": [], + "label": "ThresholdExpression", + "description": [], + "signature": [ + "(props: ", + "ThresholdExpressionProps", + ") => JSX.Element" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ThresholdExpression.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unmuteRule", + "type": "Function", + "tags": [], + "label": "unmuteRule", + "description": [], + "signature": [ + "({ id, http }: { id: string; http: ", + "HttpSetup", + "; }) => Promise" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unmute.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unmuteRule.$1", + "type": "Object", + "tags": [], + "label": "{ id, http }", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unmute.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unmuteRule.$1.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unmute.ts", "deprecated": false, "trackAdoption": false @@ -1776,6 +2157,106 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.updateActionConnector", + "type": "Function", + "tags": [], + "label": "updateActionConnector", + "description": [], + "signature": [ + "({\n http,\n connector,\n id,\n}: { http: ", + "HttpSetup", + "; connector: Pick<", + "ActionConnectorWithoutId", + ", Record>, \"name\" | \"config\" | \"secrets\">; id: string; }) => Promise<", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionConnector", + "text": "ActionConnector" + }, + ", Record>>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.updateActionConnector.$1", + "type": "Object", + "tags": [], + "label": "{\n http,\n connector,\n id,\n}", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.updateActionConnector.$1.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + "HttpSetup" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.updateActionConnector.$1.connector", + "type": "Object", + "tags": [], + "label": "connector", + "description": [], + "signature": [ + "{ name: string; config: Record; secrets: Record; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.updateActionConnector.$1.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.useConnectorContext", + "type": "Function", + "tags": [], + "label": "useConnectorContext", + "description": [], + "signature": [ + "() => ", + "ConnectorContextValue" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/context/use_connector_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.useLoadRuleTypes", @@ -1819,6 +2300,41 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.useTypedKibana", + "type": "Function", + "tags": [], + "label": "useTypedKibana", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "kibanaReact", + "scope": "public", + "docId": "kibKibanaReactPluginApi", + "section": "def-public.KibanaReactContextValue", + "text": "KibanaReactContextValue" + }, + " & ", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.TriggersAndActionsUiServices", + "text": "TriggersAndActionsUiServices" + }, + ">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/kibana/kibana_react.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.ValueExpression", @@ -1891,95 +2407,651 @@ "interfaces": [ { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType", + "id": "def-public.ActionConnectorFieldsProps", "type": "Interface", "tags": [], - "label": "ActionType", + "label": "ActionConnectorFieldsProps", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.id", - "type": "string", + "id": "def-public.ActionConnectorFieldsProps.readOnly", + "type": "boolean", "tags": [], - "label": "id", + "label": "readOnly", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.name", - "type": "string", + "id": "def-public.ActionConnectorFieldsProps.isEdit", + "type": "boolean", "tags": [], - "label": "name", + "label": "isEdit", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.enabled", - "type": "boolean", + "id": "def-public.ActionConnectorFieldsProps.registerPreSubmitValidator", + "type": "Function", "tags": [], - "label": "enabled", + "label": "registerPreSubmitValidator", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "signature": [ + "(validator: ", + "ConnectorValidationFunc", + ") => void" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, - "trackAdoption": false - }, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionConnectorFieldsProps.registerPreSubmitValidator.$1", + "type": "Function", + "tags": [], + "label": "validator", + "description": [], + "signature": [ + "ConnectorValidationFunc" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps", + "type": "Interface", + "tags": [], + "label": "ActionParamsProps", + "description": [], + "signature": [ { - "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.enabledInConfig", - "type": "boolean", + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionParamsProps", + "text": "ActionParamsProps" + }, + "" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.actionParams", + "type": "Object", + "tags": [], + "label": "actionParams", + "description": [], + "signature": [ + "{ [P in keyof TParams]?: TParams[P] | undefined; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.index", + "type": "number", + "tags": [], + "label": "index", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.editAction", + "type": "Function", + "tags": [], + "label": "editAction", + "description": [], + "signature": [ + "(key: string, value: ", + "SavedObjectAttribute", + ", index: number) => void" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.editAction.$1", + "type": "string", + "tags": [], + "label": "key", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.editAction.$2", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "SavedObjectAttribute" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.editAction.$3", + "type": "number", + "tags": [], + "label": "index", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.errors", + "type": "Object", + "tags": [], + "label": "errors", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.IErrorObject", + "text": "IErrorObject" + } + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.messageVariables", + "type": "Array", + "tags": [], + "label": "messageVariables", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.ActionVariable", + "text": "ActionVariable" + }, + "[] | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.defaultMessage", + "type": "string", + "tags": [], + "label": "defaultMessage", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.actionConnector", + "type": "CompoundType", + "tags": [], + "label": "actionConnector", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionConnector", + "text": "ActionConnector" + }, + ", Record> | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.isLoading", + "type": "CompoundType", + "tags": [], + "label": "isLoading", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.isDisabled", + "type": "CompoundType", + "tags": [], + "label": "isDisabled", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionParamsProps.showEmailSubjectAndMessage", + "type": "CompoundType", + "tags": [], + "label": "showEmailSubjectAndMessage", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType", + "type": "Interface", + "tags": [], + "label": "ActionType", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.enabledInConfig", + "type": "boolean", + "tags": [], + "label": "enabledInConfig", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.enabledInLicense", + "type": "boolean", + "tags": [], + "label": "enabledInLicense", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.minimumLicenseRequired", + "type": "CompoundType", + "tags": [], + "label": "minimumLicenseRequired", + "description": [], + "signature": [ + "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" + ], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionType.supportedFeatureIds", + "type": "Array", + "tags": [], + "label": "supportedFeatureIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel", + "type": "Interface", + "tags": [], + "label": "ActionTypeModel", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, + "" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.iconClass", + "type": "CompoundType", + "tags": [], + "label": "iconClass", + "description": [], + "signature": [ + "string | React.ComponentType<{}>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.selectMessage", + "type": "string", + "tags": [], + "label": "selectMessage", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.actionTypeTitle", + "type": "string", + "tags": [], + "label": "actionTypeTitle", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.validateParams", + "type": "Function", + "tags": [], + "label": "validateParams", + "description": [], + "signature": [ + "(actionParams: ActionParams) => Promise<", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.GenericValidationResult", + "text": "GenericValidationResult" + }, + ">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.validateParams.$1", + "type": "Uncategorized", + "tags": [], + "label": "actionParams", + "description": [], + "signature": [ + "ActionParams" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.actionConnectorFields", + "type": "CompoundType", + "tags": [], + "label": "actionConnectorFields", + "description": [], + "signature": [ + "React.LazyExoticComponent> | null" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.actionParamsFields", + "type": "Function", + "tags": [], + "label": "actionParamsFields", + "description": [], + "signature": [ + "React.ExoticComponent<(", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionParamsProps", + "text": "ActionParamsProps" + }, + " & React.RefAttributes, any, any>>) | (", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionParamsProps", + "text": "ActionParamsProps" + }, + " & { children?: React.ReactNode; })> & { readonly _result: React.ComponentType<", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionParamsProps", + "text": "ActionParamsProps" + }, + ">; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.actionParamsFields.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ActionTypeModel.defaultActionParams", + "type": "Object", "tags": [], - "label": "enabledInConfig", + "label": "defaultActionParams", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "signature": [ + "Partial | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.enabledInLicense", - "type": "boolean", + "id": "def-public.ActionTypeModel.defaultRecoveredActionParams", + "type": "Object", "tags": [], - "label": "enabledInLicense", + "label": "defaultRecoveredActionParams", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "signature": [ + "Partial | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.minimumLicenseRequired", - "type": "CompoundType", + "id": "def-public.ActionTypeModel.customConnectorSelectItem", + "type": "Object", "tags": [], - "label": "minimumLicenseRequired", + "label": "customConnectorSelectItem", "description": [], "signature": [ - "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" + "CustomConnectorSelectionItem | undefined" ], - "path": "x-pack/plugins/actions/common/types.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "triggersActionsUi", - "id": "def-public.ActionType.supportedFeatureIds", - "type": "Array", + "id": "def-public.ActionTypeModel.isExperimental", + "type": "CompoundType", "tags": [], - "label": "supportedFeatureIds", + "label": "isExperimental", "description": [], "signature": [ - "string[]" + "boolean | undefined" ], - "path": "x-pack/plugins/actions/common/types.ts", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, "trackAdoption": false } @@ -2805,6 +3877,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ConfigFieldSchema", + "type": "Interface", + "tags": [], + "label": "ConfigFieldSchema", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ConfigFieldSchema", + "text": "ConfigFieldSchema" + }, + " extends ", + "CommonFieldSchema" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ConfigFieldSchema.isUrlField", + "type": "CompoundType", + "tags": [], + "label": "isUrlField", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.FieldBrowserOptions", @@ -3010,6 +4121,44 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.GenericValidationResult", + "type": "Interface", + "tags": [], + "label": "GenericValidationResult", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.GenericValidationResult", + "text": "GenericValidationResult" + }, + "" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.GenericValidationResult.errors", + "type": "Object", + "tags": [], + "label": "errors", + "description": [], + "signature": [ + "{ [P in Extract]: unknown; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.IErrorObject", @@ -3218,11 +4367,29 @@ "description": [], "signature": [ "{ get: (id: string) => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "; list: () => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "[]; register: (objectType: ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", @@ -4147,6 +5314,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.SecretsFieldSchema", + "type": "Interface", + "tags": [], + "label": "SecretsFieldSchema", + "description": [], + "signature": [ + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.SecretsFieldSchema", + "text": "SecretsFieldSchema" + }, + " extends ", + "CommonFieldSchema" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.SecretsFieldSchema.isPasswordField", + "type": "CompoundType", + "tags": [], + "label": "isPasswordField", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.TriggersAndActionsUiServices", @@ -4390,11 +5596,29 @@ "description": [], "signature": [ "{ get: (id: string) => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "; list: () => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "[]; register: (objectType: ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", @@ -4603,6 +5827,18 @@ } ], "enums": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertProvidedActionVariables", + "type": "Enum", + "tags": [], + "label": "AlertProvidedActionVariables", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.COMPARATORS", @@ -4690,11 +5926,29 @@ "description": [], "signature": [ "{ get: (id: string) => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "; list: () => ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, "[]; register: (objectType: ", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", @@ -4717,6 +5971,63 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ALERT_HISTORY_PREFIX", + "type": "string", + "tags": [], + "label": "ALERT_HISTORY_PREFIX", + "description": [], + "signature": [ + "\"kibana-alert-history-\"" + ], + "path": "x-pack/plugins/actions/common/alert_history_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertHistoryDefaultIndexName", + "type": "string", + "tags": [], + "label": "AlertHistoryDefaultIndexName", + "description": [], + "path": "x-pack/plugins/actions/common/alert_history_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertHistoryDocumentTemplate", + "type": "CompoundType", + "tags": [], + "label": "AlertHistoryDocumentTemplate", + "description": [], + "signature": [ + "Readonly<{ event: { kind: string; }; kibana?: { alert: { actionGroupName?: string | undefined; actionGroup?: string | undefined; context?: { [x: string]: Record; } | undefined; id?: string | undefined; }; } | undefined; rule?: { type?: string | undefined; space?: string | undefined; params?: { [x: string]: Record; } | undefined; name?: string | undefined; id?: string | undefined; } | undefined; message?: unknown; tags?: string[] | undefined; '@timestamp': string; }> | null" + ], + "path": "x-pack/plugins/actions/common/alert_history_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertHistoryEsIndexConnectorId", + "type": "string", + "tags": [], + "label": "AlertHistoryEsIndexConnectorId", + "description": [], + "signature": [ + "\"preconfigured-alert-history-es-index\"" + ], + "path": "x-pack/plugins/actions/common/alert_history_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.AlertsTableConfigurationRegistryContract", @@ -4784,6 +6095,51 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.connectorDeprecatedMessage", + "type": "string", + "tags": [], + "label": "connectorDeprecatedMessage", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/common/connectors_selection.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.ConnectorFormSchema", + "type": "Type", + "tags": [], + "label": "ConnectorFormSchema", + "description": [ + "\nThe following type is equivalent to:\n\ninterface ConnectorFormSchema {\n id?: string,\n name?: string,\n actionTypeId: string,\n isDeprecated: boolean,\n config: Config,\n secrets: Secrets,\n}" + ], + "signature": [ + "Pick<", + "UserConfiguredActionConnector", + ", \"config\" | \"secrets\" | \"actionTypeId\" | \"isDeprecated\"> & Partial, \"name\" | \"id\">>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.deprecatedMessage", + "type": "string", + "tags": [], + "label": "deprecatedMessage", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/common/connectors_selection.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.GetRenderCellValue", @@ -5801,7 +7157,13 @@ "signature": [ "TypeRegistry", "<", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ">" ], "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", @@ -5873,7 +7235,13 @@ "signature": [ "TypeRegistry", "<", - "ActionTypeModel", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.ActionTypeModel", + "text": "ActionTypeModel" + }, ">" ], "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 0a3cbb630285..cfa8f8965332 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 437 | 1 | 416 | 45 | +| 513 | 1 | 486 | 48 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 2a01df283087..25fd9d32aac5 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: 2022-09-27 +date: 2022-09-28 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 f46cdf9d62ee..f85563ce18a7 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index f70e303ebe4d..2aae0fbddf27 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 2e10e0a54e2c..261ef25b80e9 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: 2022-09-27 +date: 2022-09-28 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 18b782fa7081..1e75d7da1deb 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 7a9ded502796..d2cdaf4d89c8 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: 2022-09-27 +date: 2022-09-28 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 34bbb721a372..5f6baf6d86c5 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: 2022-09-27 +date: 2022-09-28 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 c3afd8d86827..470423b8266a 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: 2022-09-27 +date: 2022-09-28 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 271d79629a0b..ff5773111414 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: 2022-09-27 +date: 2022-09-28 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 f28ab39c716f..1da13d1fcc09 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: 2022-09-27 +date: 2022-09-28 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 32a96558c72f..d30f1b6a560a 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: 2022-09-27 +date: 2022-09-28 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 827eb1f6f096..f8d894bdaa7d 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: 2022-09-27 +date: 2022-09-28 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 d9fc5b663679..deb819de77df 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: 2022-09-27 +date: 2022-09-28 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 e8dfbf76d6ff..042fb74b10af 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: 2022-09-27 +date: 2022-09-28 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 c962200fe1fd..2458fbb4d9de 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: 2022-09-27 +date: 2022-09-28 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 abe648760bf1..606b4dcf05e1 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: 2022-09-27 +date: 2022-09-28 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 4260c1aaeb25..e56cd5d8f509 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: 2022-09-27 +date: 2022-09-28 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 f03c9b37afbb..9ac63dfe30cc 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index c4ea2e6c61e7..e5907aaf8a8a 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: 2022-09-27 +date: 2022-09-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From e24fc503f661091b7dac115695453bb3b7f4e902 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 28 Sep 2022 08:59:09 +0200 Subject: [PATCH 047/185] Move `RequestHandlerContext` types and implementation to packages (#141759) * create empty packages * continue moving things * adapt usages * [CI] Auto-commit changed files from 'node scripts/generate codeowners' * renaming packages... * adapting usages again * [CI] Auto-commit changed files from 'node scripts/generate codeowners' * move preboot context to packages too * add internal test mocks * update READMEs Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 2 + package.json | 4 + packages/BUILD.bazel | 4 + .../BUILD.bazel | 116 ++++++++++++++++++ .../README.md | 3 + .../index.ts | 10 ++ .../jest.config.js | 13 ++ .../kibana.jsonc | 7 ++ .../package.json | 8 ++ .../src}/core_route_handler_context.test.ts | 33 ++--- .../src/core_route_handler_context.ts | 63 ++++++++++ .../src/index.ts | 12 ++ ...preboot_core_route_handler_context.test.ts | 6 +- .../preboot_core_route_handler_context.ts | 21 ++-- .../core_route_handler_context_params.mock.ts | 21 ++++ .../src/test_helpers/index.ts | 10 ++ ..._core_route_handler_context_params.mock.ts | 15 +++ .../tsconfig.json | 17 +++ .../BUILD.bazel | 110 +++++++++++++++++ .../README.md | 3 + .../index.ts | 16 +++ .../jest.config.js | 13 ++ .../kibana.jsonc | 7 ++ .../package.json | 8 ++ .../src/index.ts | 18 +++ .../src/preboot_request_handler_context.ts | 31 +++++ .../src/request_handler_context.ts | 41 +++---- .../tsconfig.json | 17 +++ .../http_resources/http_resources_service.ts | 2 +- src/core/server/http_resources/types.ts | 2 +- src/core/server/index.ts | 43 ++----- src/core/server/server.ts | 11 +- yarn.lock | 16 +++ 33 files changed, 607 insertions(+), 96 deletions(-) create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/BUILD.bazel create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/README.md create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/index.ts create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/jest.config.js create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/kibana.jsonc create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/package.json rename {src/core/server => packages/core/http/core-http-request-handler-context-server-internal/src}/core_route_handler_context.test.ts (86%) create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.ts create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/src/index.ts rename {src/core/server => packages/core/http/core-http-request-handler-context-server-internal/src}/preboot_core_route_handler_context.test.ts (85%) rename {src/core/server => packages/core/http/core-http-request-handler-context-server-internal/src}/preboot_core_route_handler_context.ts (71%) create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/core_route_handler_context_params.mock.ts create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/index.ts create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/preboot_core_route_handler_context_params.mock.ts create mode 100644 packages/core/http/core-http-request-handler-context-server-internal/tsconfig.json create mode 100644 packages/core/http/core-http-request-handler-context-server/BUILD.bazel create mode 100644 packages/core/http/core-http-request-handler-context-server/README.md create mode 100644 packages/core/http/core-http-request-handler-context-server/index.ts create mode 100644 packages/core/http/core-http-request-handler-context-server/jest.config.js create mode 100644 packages/core/http/core-http-request-handler-context-server/kibana.jsonc create mode 100644 packages/core/http/core-http-request-handler-context-server/package.json create mode 100644 packages/core/http/core-http-request-handler-context-server/src/index.ts create mode 100644 packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts rename src/core/server/core_route_handler_context.ts => packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts (53%) create mode 100644 packages/core/http/core-http-request-handler-context-server/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2c1a3e323a99..d802eb4024a0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -743,6 +743,8 @@ packages/core/http/core-http-browser-mocks @elastic/kibana-core packages/core/http/core-http-common @elastic/kibana-core packages/core/http/core-http-context-server-internal @elastic/kibana-core packages/core/http/core-http-context-server-mocks @elastic/kibana-core +packages/core/http/core-http-request-handler-context-server @elastic/kibana-core +packages/core/http/core-http-request-handler-context-server-internal @elastic/kibana-core packages/core/http/core-http-router-server-internal @elastic/kibana-core packages/core/http/core-http-router-server-mocks @elastic/kibana-core packages/core/http/core-http-server @elastic/kibana-core diff --git a/package.json b/package.json index 6c0913394e24..ebbff20d9173 100644 --- a/package.json +++ b/package.json @@ -213,6 +213,8 @@ "@kbn/core-http-common": "link:bazel-bin/packages/core/http/core-http-common", "@kbn/core-http-context-server-internal": "link:bazel-bin/packages/core/http/core-http-context-server-internal", "@kbn/core-http-context-server-mocks": "link:bazel-bin/packages/core/http/core-http-context-server-mocks", + "@kbn/core-http-request-handler-context-server": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server", + "@kbn/core-http-request-handler-context-server-internal": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal", "@kbn/core-http-router-server-internal": "link:bazel-bin/packages/core/http/core-http-router-server-internal", "@kbn/core-http-router-server-mocks": "link:bazel-bin/packages/core/http/core-http-router-server-mocks", "@kbn/core-http-server": "link:bazel-bin/packages/core/http/core-http-server", @@ -934,6 +936,8 @@ "@types/kbn__core-http-common": "link:bazel-bin/packages/core/http/core-http-common/npm_module_types", "@types/kbn__core-http-context-server-internal": "link:bazel-bin/packages/core/http/core-http-context-server-internal/npm_module_types", "@types/kbn__core-http-context-server-mocks": "link:bazel-bin/packages/core/http/core-http-context-server-mocks/npm_module_types", + "@types/kbn__core-http-request-handler-context-server": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server/npm_module_types", + "@types/kbn__core-http-request-handler-context-server-internal": "link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal/npm_module_types", "@types/kbn__core-http-router-server-internal": "link:bazel-bin/packages/core/http/core-http-router-server-internal/npm_module_types", "@types/kbn__core-http-router-server-mocks": "link:bazel-bin/packages/core/http/core-http-router-server-mocks/npm_module_types", "@types/kbn__core-http-server": "link:bazel-bin/packages/core/http/core-http-server/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 62cc4b4ce9ad..455b851a4936 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -79,6 +79,8 @@ filegroup( "//packages/core/http/core-http-common:build", "//packages/core/http/core-http-context-server-internal:build", "//packages/core/http/core-http-context-server-mocks:build", + "//packages/core/http/core-http-request-handler-context-server:build", + "//packages/core/http/core-http-request-handler-context-server-internal:build", "//packages/core/http/core-http-router-server-internal:build", "//packages/core/http/core-http-router-server-mocks:build", "//packages/core/http/core-http-server:build", @@ -410,6 +412,8 @@ filegroup( "//packages/core/http/core-http-common:build_types", "//packages/core/http/core-http-context-server-internal:build_types", "//packages/core/http/core-http-context-server-mocks:build_types", + "//packages/core/http/core-http-request-handler-context-server:build_types", + "//packages/core/http/core-http-request-handler-context-server-internal:build_types", "//packages/core/http/core-http-router-server-internal:build_types", "//packages/core/http/core-http-router-server-mocks:build_types", "//packages/core/http/core-http-server:build_types", diff --git a/packages/core/http/core-http-request-handler-context-server-internal/BUILD.bazel b/packages/core/http/core-http-request-handler-context-server-internal/BUILD.bazel new file mode 100644 index 000000000000..82040b5fb1ad --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/BUILD.bazel @@ -0,0 +1,116 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-http-request-handler-context-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-http-request-handler-context-server-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/core/elasticsearch/core-elasticsearch-server-internal", + "//packages/core/saved-objects/core-saved-objects-server-internal", + "//packages/core/deprecations/core-deprecations-server-internal", + "//packages/core/ui-settings/core-ui-settings-server-internal", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/http/core-http-request-handler-context-server:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-server-internal:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-server-internal:npm_module_types", + "//packages/core/deprecations/core-deprecations-server-internal:npm_module_types", + "//packages/core/ui-settings/core-ui-settings-server:npm_module_types", + "//packages/core/ui-settings/core-ui-settings-server-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/http/core-http-request-handler-context-server-internal/README.md b/packages/core/http/core-http-request-handler-context-server-internal/README.md new file mode 100644 index 000000000000..aa27c116c4f6 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-http-request-handler-context-server-internal + +This package contains the internal types and implementation of Core's `CoreRequestHandlerContext` diff --git a/packages/core/http/core-http-request-handler-context-server-internal/index.ts b/packages/core/http/core-http-request-handler-context-server-internal/index.ts new file mode 100644 index 000000000000..2ee514f67265 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CoreRouteHandlerContext, PrebootCoreRouteHandlerContext } from './src'; +export type { CoreRouteHandlerContextParams, PrebootCoreRouteHandlerContextParams } from './src'; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/jest.config.js b/packages/core/http/core-http-request-handler-context-server-internal/jest.config.js new file mode 100644 index 000000000000..68a5f2b3a03a --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/http/core-http-request-handler-context-server-internal'], +}; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/kibana.jsonc b/packages/core/http/core-http-request-handler-context-server-internal/kibana.jsonc new file mode 100644 index 000000000000..98b452aec0d9 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-request-handler-context-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/http/core-http-request-handler-context-server-internal/package.json b/packages/core/http/core-http-request-handler-context-server-internal/package.json new file mode 100644 index 000000000000..672bb6ce7271 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-http-request-handler-context-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/core_route_handler_context.test.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.test.ts similarity index 86% rename from src/core/server/core_route_handler_context.test.ts rename to packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.test.ts index ace0144eae54..f8e906650fcf 100644 --- a/src/core/server/core_route_handler_context.test.ts +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.test.ts @@ -7,13 +7,14 @@ */ import { CoreRouteHandlerContext } from './core_route_handler_context'; -import { coreMock, httpServerMock } from './mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { createCoreRouteHandlerContextParamsMock } from './test_helpers'; describe('#elasticsearch', () => { describe('#client', () => { test('returns the results of coreStart.elasticsearch.client.asScoped', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client = context.elasticsearch.client; @@ -22,7 +23,7 @@ describe('#elasticsearch', () => { test('lazily created', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); expect(coreStart.elasticsearch.client.asScoped).not.toHaveBeenCalled(); @@ -33,7 +34,7 @@ describe('#elasticsearch', () => { test('only creates one instance', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client1 = context.elasticsearch.client; @@ -50,7 +51,7 @@ describe('#savedObjects', () => { describe('#client', () => { test('returns the results of coreStart.savedObjects.getScopedClient', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client = context.savedObjects.client; @@ -59,7 +60,7 @@ describe('#savedObjects', () => { test('lazily created', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const savedObjects = context.savedObjects; @@ -71,7 +72,7 @@ describe('#savedObjects', () => { test('only creates one instance', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client1 = context.savedObjects.client; @@ -86,7 +87,7 @@ describe('#savedObjects', () => { describe('#typeRegistry', () => { test('returns the results of coreStart.savedObjects.getTypeRegistry', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const typeRegistry = context.savedObjects.typeRegistry; @@ -95,7 +96,7 @@ describe('#savedObjects', () => { test('lazily created', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); expect(coreStart.savedObjects.getTypeRegistry).not.toHaveBeenCalled(); @@ -106,7 +107,7 @@ describe('#savedObjects', () => { test('only creates one instance', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const typeRegistry1 = context.savedObjects.typeRegistry; @@ -123,7 +124,7 @@ describe('#uiSettings', () => { describe('#client', () => { test('returns the results of coreStart.uiSettings.asScopedToClient', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client = context.uiSettings.client; @@ -132,7 +133,7 @@ describe('#uiSettings', () => { test('lazily created', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); expect(coreStart.uiSettings.asScopedToClient).not.toHaveBeenCalled(); @@ -143,7 +144,7 @@ describe('#uiSettings', () => { test('only creates one instance', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client1 = context.uiSettings.client; @@ -160,7 +161,7 @@ describe('#deprecations', () => { describe('#client', () => { test('returns the results of coreStart.deprecations.asScopedToClient', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client = context.deprecations.client; @@ -169,7 +170,7 @@ describe('#deprecations', () => { test('lazily created', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); expect(coreStart.deprecations.asScopedToClient).not.toHaveBeenCalled(); @@ -180,7 +181,7 @@ describe('#deprecations', () => { test('only creates one instance', () => { const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); + const coreStart = createCoreRouteHandlerContextParamsMock(); const context = new CoreRouteHandlerContext(coreStart, request); const client1 = context.deprecations.client; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.ts new file mode 100644 index 000000000000..6cc54130a575 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/core_route_handler_context.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { CoreRequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import { + CoreElasticsearchRouteHandlerContext, + type InternalElasticsearchServiceStart, +} from '@kbn/core-elasticsearch-server-internal'; +import { + CoreSavedObjectsRouteHandlerContext, + type InternalSavedObjectsServiceStart, +} from '@kbn/core-saved-objects-server-internal'; +import { + CoreDeprecationsRouteHandlerContext, + type InternalDeprecationsServiceStart, +} from '@kbn/core-deprecations-server-internal'; +import { + CoreUiSettingsRouteHandlerContext, + type InternalUiSettingsServiceStart, +} from '@kbn/core-ui-settings-server-internal'; + +/** + * Subset of `InternalCoreStart` used by {@link CoreRouteHandlerContext} + * @internal + */ +export interface CoreRouteHandlerContextParams { + elasticsearch: InternalElasticsearchServiceStart; + savedObjects: InternalSavedObjectsServiceStart; + uiSettings: InternalUiSettingsServiceStart; + deprecations: InternalDeprecationsServiceStart; +} + +/** + * The concrete implementation for Core's route handler context. + * + * @internal + */ +export class CoreRouteHandlerContext implements CoreRequestHandlerContext { + readonly elasticsearch: CoreElasticsearchRouteHandlerContext; + readonly savedObjects: CoreSavedObjectsRouteHandlerContext; + readonly uiSettings: CoreUiSettingsRouteHandlerContext; + readonly deprecations: CoreDeprecationsRouteHandlerContext; + + constructor(coreStart: CoreRouteHandlerContextParams, request: KibanaRequest) { + this.elasticsearch = new CoreElasticsearchRouteHandlerContext(coreStart.elasticsearch, request); + this.savedObjects = new CoreSavedObjectsRouteHandlerContext(coreStart.savedObjects, request); + this.uiSettings = new CoreUiSettingsRouteHandlerContext( + coreStart.uiSettings, + this.savedObjects + ); + this.deprecations = new CoreDeprecationsRouteHandlerContext( + coreStart.deprecations, + this.elasticsearch, + this.savedObjects + ); + } +} diff --git a/packages/core/http/core-http-request-handler-context-server-internal/src/index.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/index.ts new file mode 100644 index 000000000000..48d93f78b2a4 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/index.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CoreRouteHandlerContext } from './core_route_handler_context'; +export type { CoreRouteHandlerContextParams } from './core_route_handler_context'; +export { PrebootCoreRouteHandlerContext } from './preboot_core_route_handler_context'; +export type { PrebootCoreRouteHandlerContextParams } from './preboot_core_route_handler_context'; diff --git a/src/core/server/preboot_core_route_handler_context.test.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.test.ts similarity index 85% rename from src/core/server/preboot_core_route_handler_context.test.ts rename to packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.test.ts index 8d090d864463..75bfc39bfdc9 100644 --- a/src/core/server/preboot_core_route_handler_context.test.ts +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.test.ts @@ -7,12 +7,12 @@ */ import { PrebootCoreRouteHandlerContext } from './preboot_core_route_handler_context'; -import { coreMock } from './mocks'; +import { createPrebootCoreRouteHandlerContextParamsMock } from './test_helpers'; describe('#uiSettings', () => { describe('#client', () => { test('returns the results of corePreboot.uiSettings.createDefaultsClient', () => { - const corePreboot = coreMock.createInternalPreboot(); + const corePreboot = createPrebootCoreRouteHandlerContextParamsMock(); const context = new PrebootCoreRouteHandlerContext(corePreboot); const client = context.uiSettings.client; @@ -21,7 +21,7 @@ describe('#uiSettings', () => { }); test('only creates one instance', () => { - const corePreboot = coreMock.createInternalPreboot(); + const corePreboot = createPrebootCoreRouteHandlerContextParamsMock(); const context = new PrebootCoreRouteHandlerContext(corePreboot); const client1 = context.uiSettings.client; diff --git a/src/core/server/preboot_core_route_handler_context.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.ts similarity index 71% rename from src/core/server/preboot_core_route_handler_context.ts rename to packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.ts index ec5ef594180d..c0bf184d8c98 100644 --- a/src/core/server/preboot_core_route_handler_context.ts +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/preboot_core_route_handler_context.ts @@ -8,13 +8,17 @@ // eslint-disable-next-line max-classes-per-file import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; -import type { InternalCorePreboot } from './internal_types'; +import type { + PrebootUiSettingsRequestHandlerContext, + PrebootCoreRequestHandlerContext, +} from '@kbn/core-http-request-handler-context-server'; +import type { InternalUiSettingsServicePreboot } from '@kbn/core-ui-settings-server-internal'; /** - * @public + * @internal */ -export interface PrebootUiSettingsRequestHandlerContext { - client: IUiSettingsClient; +export interface PrebootCoreRouteHandlerContextParams { + uiSettings: InternalUiSettingsServicePreboot; } /** @@ -25,13 +29,6 @@ class PrebootCoreUiSettingsRouteHandlerContext implements PrebootUiSettingsReque constructor(public readonly client: IUiSettingsClient) {} } -/** - * @public - */ -export interface PrebootCoreRequestHandlerContext { - uiSettings: PrebootUiSettingsRequestHandlerContext; -} - /** * Implementation of {@link PrebootCoreRequestHandlerContext}. * @internal @@ -39,7 +36,7 @@ export interface PrebootCoreRequestHandlerContext { export class PrebootCoreRouteHandlerContext implements PrebootCoreRequestHandlerContext { readonly uiSettings: PrebootUiSettingsRequestHandlerContext; - constructor(private readonly corePreboot: InternalCorePreboot) { + constructor(private readonly corePreboot: PrebootCoreRouteHandlerContextParams) { this.uiSettings = new PrebootCoreUiSettingsRouteHandlerContext( this.corePreboot.uiSettings.createDefaultsClient() ); diff --git a/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/core_route_handler_context_params.mock.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/core_route_handler_context_params.mock.ts new file mode 100644 index 000000000000..eaf46eae3c8b --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/core_route_handler_context_params.mock.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { savedObjectsServiceMock } from '@kbn/core-saved-objects-server-mocks'; +import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; +import { deprecationsServiceMock } from '@kbn/core-deprecations-server-mocks'; + +export const createCoreRouteHandlerContextParamsMock = () => { + return { + elasticsearch: elasticsearchServiceMock.createInternalStart(), + savedObjects: savedObjectsServiceMock.createInternalStartContract(), + uiSettings: uiSettingsServiceMock.createStartContract(), + deprecations: deprecationsServiceMock.createInternalStartContract(), + }; +}; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/index.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/index.ts new file mode 100644 index 000000000000..5556547d4602 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { createCoreRouteHandlerContextParamsMock } from './core_route_handler_context_params.mock'; +export { createPrebootCoreRouteHandlerContextParamsMock } from './preboot_core_route_handler_context_params.mock'; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/preboot_core_route_handler_context_params.mock.ts b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/preboot_core_route_handler_context_params.mock.ts new file mode 100644 index 000000000000..dc4eab6b5567 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/src/test_helpers/preboot_core_route_handler_context_params.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; + +export const createPrebootCoreRouteHandlerContextParamsMock = () => { + return { + uiSettings: uiSettingsServiceMock.createPrebootContract(), + }; +}; diff --git a/packages/core/http/core-http-request-handler-context-server-internal/tsconfig.json b/packages/core/http/core-http-request-handler-context-server-internal/tsconfig.json new file mode 100644 index 000000000000..71bb40fe57f3 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server-internal/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/http/core-http-request-handler-context-server/BUILD.bazel b/packages/core/http/core-http-request-handler-context-server/BUILD.bazel new file mode 100644 index 000000000000..45c5ebc08776 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/BUILD.bazel @@ -0,0 +1,110 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-http-request-handler-context-server" +PKG_REQUIRE_NAME = "@kbn/core-http-request-handler-context-server" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-server:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-server:npm_module_types", + "//packages/core/deprecations/core-deprecations-server:npm_module_types", + "//packages/core/ui-settings/core-ui-settings-server:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/http/core-http-request-handler-context-server/README.md b/packages/core/http/core-http-request-handler-context-server/README.md new file mode 100644 index 000000000000..b7b191f7345f --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/README.md @@ -0,0 +1,3 @@ +# @kbn/core-http-request-handler-context-server + +This package contains the public types for Core's server-side `RequestHandlerContext` http subdomain. diff --git a/packages/core/http/core-http-request-handler-context-server/index.ts b/packages/core/http/core-http-request-handler-context-server/index.ts new file mode 100644 index 000000000000..125c466d25d2 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { + RequestHandlerContext, + CoreRequestHandlerContext, + CustomRequestHandlerContext, + PrebootCoreRequestHandlerContext, + PrebootRequestHandlerContext, + PrebootUiSettingsRequestHandlerContext, +} from './src'; diff --git a/packages/core/http/core-http-request-handler-context-server/jest.config.js b/packages/core/http/core-http-request-handler-context-server/jest.config.js new file mode 100644 index 000000000000..dc60767ed088 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/http/core-http-request-handler-context-server'], +}; diff --git a/packages/core/http/core-http-request-handler-context-server/kibana.jsonc b/packages/core/http/core-http-request-handler-context-server/kibana.jsonc new file mode 100644 index 000000000000..3fba38b6444e --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-http-request-handler-context-server", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/http/core-http-request-handler-context-server/package.json b/packages/core/http/core-http-request-handler-context-server/package.json new file mode 100644 index 000000000000..da85fc826828 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-http-request-handler-context-server", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/http/core-http-request-handler-context-server/src/index.ts b/packages/core/http/core-http-request-handler-context-server/src/index.ts new file mode 100644 index 000000000000..2ab372a0ab97 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { + RequestHandlerContext, + CoreRequestHandlerContext, + CustomRequestHandlerContext, +} from './request_handler_context'; +export type { + PrebootRequestHandlerContext, + PrebootCoreRequestHandlerContext, + PrebootUiSettingsRequestHandlerContext, +} from './preboot_request_handler_context'; diff --git a/packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts b/packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts new file mode 100644 index 000000000000..62caefe5619c --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { RequestHandlerContextBase } from '@kbn/core-http-server'; +import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; + +/** + * @public + */ +export interface PrebootUiSettingsRequestHandlerContext { + client: IUiSettingsClient; +} + +/** + * @public + */ +export interface PrebootCoreRequestHandlerContext { + uiSettings: PrebootUiSettingsRequestHandlerContext; +} + +/** + * @public + */ +export interface PrebootRequestHandlerContext extends RequestHandlerContextBase { + core: Promise; +} diff --git a/src/core/server/core_route_handler_context.ts b/packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts similarity index 53% rename from src/core/server/core_route_handler_context.ts rename to packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts index 14cc99b50143..07704752bd8e 100644 --- a/src/core/server/core_route_handler_context.ts +++ b/packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts @@ -6,16 +6,11 @@ * Side Public License, v 1. */ -import type { KibanaRequest } from '@kbn/core-http-server'; +import type { RequestHandlerContextBase } from '@kbn/core-http-server'; import type { ElasticsearchRequestHandlerContext } from '@kbn/core-elasticsearch-server'; -import { CoreElasticsearchRouteHandlerContext } from '@kbn/core-elasticsearch-server-internal'; import type { SavedObjectsRequestHandlerContext } from '@kbn/core-saved-objects-server'; -import { CoreSavedObjectsRouteHandlerContext } from '@kbn/core-saved-objects-server-internal'; import type { DeprecationsRequestHandlerContext } from '@kbn/core-deprecations-server'; -import { CoreDeprecationsRouteHandlerContext } from '@kbn/core-deprecations-server-internal'; import type { UiSettingsRequestHandlerContext } from '@kbn/core-ui-settings-server'; -import { CoreUiSettingsRouteHandlerContext } from '@kbn/core-ui-settings-server-internal'; -import type { InternalCoreStart } from './internal_types'; /** * The `core` context provided to route handler. @@ -39,27 +34,19 @@ export interface CoreRequestHandlerContext { } /** - * The concrete implementation for Core's route handler context. + * Base context passed to a route handler, containing the `core` context part. * - * @internal + * @public */ -export class CoreRouteHandlerContext implements CoreRequestHandlerContext { - readonly elasticsearch: CoreElasticsearchRouteHandlerContext; - readonly savedObjects: CoreSavedObjectsRouteHandlerContext; - readonly uiSettings: CoreUiSettingsRouteHandlerContext; - readonly deprecations: CoreDeprecationsRouteHandlerContext; - - constructor(coreStart: InternalCoreStart, request: KibanaRequest) { - this.elasticsearch = new CoreElasticsearchRouteHandlerContext(coreStart.elasticsearch, request); - this.savedObjects = new CoreSavedObjectsRouteHandlerContext(coreStart.savedObjects, request); - this.uiSettings = new CoreUiSettingsRouteHandlerContext( - coreStart.uiSettings, - this.savedObjects - ); - this.deprecations = new CoreDeprecationsRouteHandlerContext( - coreStart.deprecations, - this.elasticsearch, - this.savedObjects - ); - } +export interface RequestHandlerContext extends RequestHandlerContextBase { + core: Promise; } + +/** + * Mixin allowing plugins to define their own request handler contexts. + * + * @public + */ +export type CustomRequestHandlerContext = RequestHandlerContext & { + [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; +}; diff --git a/packages/core/http/core-http-request-handler-context-server/tsconfig.json b/packages/core/http/core-http-request-handler-context-server/tsconfig.json new file mode 100644 index 000000000000..71bb40fe57f3 --- /dev/null +++ b/packages/core/http/core-http-request-handler-context-server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts index 7e95dc9e302d..5db20bf45e40 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/src/core/server/http_resources/http_resources_service.ts @@ -18,7 +18,7 @@ import type { InternalHttpServiceSetup, InternalHttpServicePreboot, } from '@kbn/core-http-server-internal'; -import { RequestHandlerContext } from '..'; +import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; import { InternalRenderingServicePreboot, InternalRenderingServiceSetup } from '../rendering'; import { InternalHttpResourcesSetup, diff --git a/src/core/server/http_resources/types.ts b/src/core/server/http_resources/types.ts index 397f03098dd1..246a7394c3ad 100644 --- a/src/core/server/http_resources/types.ts +++ b/src/core/server/http_resources/types.ts @@ -15,7 +15,7 @@ import type { KibanaResponseFactory, RequestHandler, } from '@kbn/core-http-server'; -import type { RequestHandlerContext } from '..'; +import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; /** * Allows to configure HTTP response parameters diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 6e72b1d2623c..020975c15fb1 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -42,7 +42,6 @@ import type { ExecutionContextStart, } from '@kbn/core-execution-context-server'; import type { - RequestHandlerContextBase, IRouter, RequestHandler, KibanaResponseFactory, @@ -69,12 +68,11 @@ import type { CoreUsageDataStart, CoreUsageDataSetup } from '@kbn/core-usage-dat import type { I18nServiceSetup } from '@kbn/core-i18n-server'; import type { StatusServiceSetup } from '@kbn/core-status-server'; import type { UiSettingsServiceSetup, UiSettingsServiceStart } from '@kbn/core-ui-settings-server'; - +import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; import { HttpResources } from './http_resources'; -import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins'; -import type { CoreRequestHandlerContext } from './core_route_handler_context'; -import type { PrebootCoreRequestHandlerContext } from './preboot_core_route_handler_context'; +import { PluginsServiceSetup, PluginsServiceStart } from './plugins'; +export type { PluginOpaqueId } from '@kbn/core-base-common'; export type { CoreUsageStats, CoreUsageData, @@ -462,33 +460,13 @@ export type { AnalyticsServicePreboot, AnalyticsServiceStart, } from '@kbn/core-analytics-server'; - -export type { CoreRequestHandlerContext } from './core_route_handler_context'; - -/** - * Base context passed to a route handler, containing the `core` context part. - * - * @public - */ -export interface RequestHandlerContext extends RequestHandlerContextBase { - core: Promise; -} - -/** - * @internal - */ -export interface PrebootRequestHandlerContext extends RequestHandlerContextBase { - core: Promise; -} - -/** - * Mixin allowing plugins to define their own request handler contexts. - * - * @public - */ -export type CustomRequestHandlerContext = RequestHandlerContext & { - [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; -}; +export type { + RequestHandlerContext, + CoreRequestHandlerContext, + CustomRequestHandlerContext, + PrebootRequestHandlerContext, + PrebootCoreRequestHandlerContext, +} from '@kbn/core-http-request-handler-context-server'; /** * Context passed to the `setup` method of `preboot` plugins. @@ -599,7 +577,6 @@ export type { HttpResources, PluginsServiceSetup, PluginsServiceStart, - PluginOpaqueId, }; /** diff --git a/src/core/server/server.ts b/src/core/server/server.ts index aa747a1023b3..e333e8b81a40 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -58,16 +58,21 @@ import { import { CoreUsageDataService } from '@kbn/core-usage-data-server-internal'; import { StatusService, statusConfig } from '@kbn/core-status-server-internal'; import { UiSettingsService, uiSettingsConfig } from '@kbn/core-ui-settings-server-internal'; +import { + CoreRouteHandlerContext, + PrebootCoreRouteHandlerContext, +} from '@kbn/core-http-request-handler-context-server-internal'; +import type { + RequestHandlerContext, + PrebootRequestHandlerContext, +} from '@kbn/core-http-request-handler-context-server'; import { CoreApp } from './core_app'; import { HttpResourcesService } from './http_resources'; import { RenderingService } from './rendering'; import { PluginsService, config as pluginsConfig } from './plugins'; import { InternalCorePreboot, InternalCoreSetup, InternalCoreStart } from './internal_types'; -import { CoreRouteHandlerContext } from './core_route_handler_context'; -import { PrebootCoreRouteHandlerContext } from './preboot_core_route_handler_context'; import { DiscoveredPlugins } from './plugins'; -import type { RequestHandlerContext, PrebootRequestHandlerContext } from '.'; const coreId = Symbol('core'); const rootConfigPath = ''; diff --git a/yarn.lock b/yarn.lock index f809bc0cac46..085f55146d4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2954,6 +2954,14 @@ version "0.0.0" uid "" +"@kbn/core-http-request-handler-context-server-internal@link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal": + version "0.0.0" + uid "" + +"@kbn/core-http-request-handler-context-server@link:bazel-bin/packages/core/http/core-http-request-handler-context-server": + version "0.0.0" + uid "" + "@kbn/core-http-router-server-internal@link:bazel-bin/packages/core/http/core-http-router-server-internal": version "0.0.0" uid "" @@ -7075,6 +7083,14 @@ version "0.0.0" uid "" +"@types/kbn__core-http-request-handler-context-server-internal@link:bazel-bin/packages/core/http/core-http-request-handler-context-server-internal/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-http-request-handler-context-server@link:bazel-bin/packages/core/http/core-http-request-handler-context-server/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-http-router-server-internal@link:bazel-bin/packages/core/http/core-http-router-server-internal/npm_module_types": version "0.0.0" uid "" From 5c2476a5bbb1296bb0a8c2adb3fa830fbb459f7f Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Wed, 28 Sep 2022 00:07:02 -0700 Subject: [PATCH 048/185] Fix getFilter not returning excluded ids when there is no rules list filter (#142033) --- .../hooks/use_bulk_edit_select.test.tsx | 16 ++++++++++++++++ .../application/hooks/use_bulk_edit_select.tsx | 11 +++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx index b0e92a3d3a84..07bf3516fbde 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx @@ -86,6 +86,22 @@ describe('useBulkEditSelectTest', () => { expect(result.current.getFilter()).toEqual(null); }); + it('getFilter should return rule list filter when selecting all with excluded ids', async () => { + const { result } = renderHook(() => + useBulkEditSelect({ + items, + totalItemCount: 4, + }) + ); + + act(() => { + result.current.onSelectAll(); + result.current.onSelectRow(items[0]); + }); + + expect(result.current.getFilter()?.arguments.length).toEqual(1); + }); + it('getFilter should return rule list filter when selecting all', async () => { const { result } = renderHook(() => useBulkEditSelect({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx index 1e2ff7496907..31c248ae1254 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx @@ -192,14 +192,13 @@ export function useBulkEditSelect(props: UseBulkEditSelectProps) { }); if (idsToExclude && idsToExclude.length) { + const excludeFilter = fromKueryExpression( + `NOT (${idsToExclude.map((id) => `alert.id: "alert:${id}"`).join(' or ')})` + ); if (ruleFilterKueryNode) { - return nodeBuilder.and([ - ruleFilterKueryNode, - fromKueryExpression( - `NOT (${idsToExclude.map((id) => `alert.id: "alert:${id}"`).join(' or ')})` - ), - ]); + return nodeBuilder.and([ruleFilterKueryNode, excludeFilter]); } + return excludeFilter; } return ruleFilterKueryNode; From e8ebc23dca3be8c2bcda15cda7991e9e5aff4df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 28 Sep 2022 09:08:36 +0200 Subject: [PATCH 049/185] [fleet] Use snapshot registry in dev mode (#141797) * [fleet] Use snapshot registry in dev mode * Update registry_url.ts * Update registry_url.ts * Fix jest test --- .../plugins/fleet/server/services/epm/registry/index.test.ts | 1 + .../plugins/fleet/server/services/epm/registry/registry_url.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts index 6504b6548f07..bf5fe89574b8 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts @@ -35,6 +35,7 @@ jest.mock('../..', () => ({ getKibanaBranch: () => 'main', getKibanaVersion: () => '99.0.0', getConfig: () => ({}), + getIsProductionMode: () => false, }, })); diff --git a/x-pack/plugins/fleet/server/services/epm/registry/registry_url.ts b/x-pack/plugins/fleet/server/services/epm/registry/registry_url.ts index 407a61ad7a91..31bb776ee1d6 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/registry_url.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/registry_url.ts @@ -21,7 +21,8 @@ const SNAPSHOT_REGISTRY_URL_CDN = 'https://epr-snapshot.elastic.co'; const getDefaultRegistryUrl = (): string => { const branch = appContextService.getKibanaBranch(); - if (branch === 'main') { + const isProduction = appContextService.getIsProductionMode(); + if (!isProduction || branch === 'main') { return SNAPSHOT_REGISTRY_URL_CDN; } else if (appContextService.getKibanaVersion().includes('-SNAPSHOT')) { return STAGING_REGISTRY_URL_CDN; From 2b094777593e715aeac6fb9678eb48ccd03b5a7a Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 28 Sep 2022 09:06:15 +0100 Subject: [PATCH 050/185] [ML] Improving log pattern analysis messaging (#141130) * [ML] Improving log pattern analysis messaging * moving to separate component * translations * fixing unselection when time range changes * changes based on review --- .../log_categorization/information_text.tsx | 101 ++++++++++++++++++ .../log_categorization_page.tsx | 14 ++- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx diff --git a/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx new file mode 100644 index 000000000000..bf80b55fee37 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { FC } from 'react'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +interface Props { + eventRateLength: number; + fieldSelected: boolean; + categoriesLength: number | null; + loading: boolean; +} + +export const InformationText: FC = ({ + eventRateLength, + fieldSelected, + categoriesLength, + loading, +}) => { + if (loading === true) { + return null; + } + return ( + <> + {eventRateLength === 0 ? ( + + + + } + titleSize="xs" + body={ +

    + +

    + } + data-test-subj="aiopsNoWindowParametersEmptyPrompt" + /> + ) : null} + + {eventRateLength > 0 && categoriesLength === null ? ( + + + + } + titleSize="xs" + body={ +

    + +

    + } + data-test-subj="aiopsNoWindowParametersEmptyPrompt" + /> + ) : null} + + {eventRateLength > 0 && categoriesLength !== null && categoriesLength === 0 ? ( + + + + } + titleSize="xs" + body={ +

    + +

    + } + data-test-subj="aiopsNoWindowParametersEmptyPrompt" + /> + ) : null} + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 845a4239f110..4eb6da0e58b9 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -40,6 +40,7 @@ import { useCategorizeRequest } from './use_categorize_request'; import type { EventRate, Category, SparkLinesPerCategory } from './use_categorize_request'; import { CategoryTable } from './category_table'; import { DocumentCountChart } from './document_count_chart'; +import { InformationText } from './information_text'; export interface LogCategorizationPageProps { dataView: DataView; @@ -160,6 +161,7 @@ export const LogCategorizationPage: FC = ({ docCount, })) ); + setCategories(null); setTotalCount(documentStats.totalCount); } }, [documentStats, earliest, latest, searchQueryLanguage, searchString, searchQuery]); @@ -210,6 +212,7 @@ export const LogCategorizationPage: FC = ({ ]); const onFieldChange = (value: EuiComboBoxOptionOption[] | undefined) => { + setCategories(null); setSelectedField(value && value.length ? value[0].label : undefined); }; @@ -313,8 +316,17 @@ export const LogCategorizationPage: FC = ({ ) : null} + {loading === true ? : null} - {categories !== null ? ( + + + + {selectedField !== undefined && categories !== null && categories.length > 0 ? ( Date: Wed, 28 Sep 2022 10:11:37 +0200 Subject: [PATCH 051/185] [Fleet] Fix for bulk update tags, updated API tests (#141905) * added action_status check for api tests * fix for update tags quickly * fixed test * updated tag labels * close bulk action menu when closing Add / remove tags popover * fixed test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/bulk_actions.tsx | 1 + .../components/tags_add_remove.test.tsx | 53 +++++++++++++++++++ .../components/tags_add_remove.tsx | 25 ++++++--- .../fleet/server/services/agents/actions.ts | 1 + .../apis/agents/reassign.ts | 17 ++++-- .../apis/agents/unenroll.ts | 7 +++ .../apis/agents/update_agent_tags.ts | 48 +++++++++++++---- .../apis/agents/upgrade.ts | 13 +++-- 8 files changed, 142 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx index bb0ec90f2b88..10ced1a5c032 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx @@ -228,6 +228,7 @@ export const AgentBulkActions: React.FunctionComponent = ({ }} onClosePopover={() => { setIsTagAddVisible(false); + closeMenu(); }} /> )} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.test.tsx index 8c4f9f3003c8..465db5236338 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.test.tsx @@ -270,6 +270,59 @@ describe('TagsAddRemove', () => { ); }); + it('should add new tag twice quickly when not found in search and button clicked - bulk selection', () => { + mockBulkUpdateTags.mockImplementation((agents, tagsToAdd, tagsToRemove, onSuccess) => + onSuccess(false) + ); + + const result = renderComponent(undefined, 'query'); + const searchInput = result.getByRole('combobox'); + + fireEvent.input(searchInput, { + target: { value: 'newTag' }, + }); + + fireEvent.click(result.getAllByText('Create a new tag "newTag"')[0].closest('button')!); + + fireEvent.input(searchInput, { + target: { value: 'newTag2' }, + }); + + fireEvent.click(result.getAllByText('Create a new tag "newTag2"')[0].closest('button')!); + + expect(mockBulkUpdateTags).toHaveBeenCalledWith( + 'query', + ['newTag2', 'newTag'], + [], + expect.anything(), + 'Tag created', + 'Tag creation failed' + ); + }); + + it('should remove tags twice quickly on bulk selection', () => { + selectedTags = ['tag1', 'tag2']; + mockBulkUpdateTags.mockImplementation((agents, tagsToAdd, tagsToRemove, onSuccess) => + onSuccess(false) + ); + + const result = renderComponent(undefined, ''); + const getTag = (name: string) => result.getByText(name); + + fireEvent.click(getTag('tag1')); + + fireEvent.click(getTag('tag2')); + + expect(mockBulkUpdateTags).toHaveBeenCalledWith( + '', + [], + ['tag2', 'tag1'], + expect.anything(), + undefined, + undefined + ); + }); + it('should make tag options button visible on mouse enter', async () => { const result = renderComponent('agent1'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx index a03ec3808e9a..70b4da44dad6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx @@ -6,7 +6,7 @@ */ import React, { Fragment, useEffect, useState, useMemo, useCallback } from 'react'; -import { difference } from 'lodash'; +import { difference, uniq } from 'lodash'; import styled from 'styled-components'; import type { EuiSelectableOption } from '@elastic/eui'; import { @@ -95,8 +95,9 @@ export const TagsAddRemove: React.FC = ({ if (hasCompleted) { return onTagsUpdated(); } - const newSelectedTags = difference(selectedTags, tagsToRemove).concat(tagsToAdd); - const allTagsWithNew = allTags.includes(tagsToAdd[0]) ? allTags : allTags.concat(tagsToAdd); + const selected = labels.filter((tag) => tag.checked === 'on').map((tag) => tag.label); + const newSelectedTags = difference(selected, tagsToRemove).concat(tagsToAdd); + const allTagsWithNew = uniq(allTags.concat(newSelectedTags)); const allTagsWithRemove = isRenameOrDelete ? difference(allTagsWithNew, tagsToRemove) : allTagsWithNew; @@ -109,8 +110,8 @@ export const TagsAddRemove: React.FC = ({ successMessage?: string, errorMessage?: string ) => { - const newSelectedTags = difference(selectedTags, tagsToRemove).concat(tagsToAdd); if (agentId) { + const newSelectedTags = difference(selectedTags, tagsToRemove).concat(tagsToAdd); updateTagsHook.updateTags( agentId, newSelectedTags, @@ -119,10 +120,22 @@ export const TagsAddRemove: React.FC = ({ errorMessage ); } else { + // sending updated tags to add/remove, in case multiple actions are done quickly and the first one is not yet propagated + const updatedTagsToAdd = tagsToAdd.concat( + labels + .filter((tag) => tag.checked === 'on' && !selectedTags.includes(tag.label)) + .map((tag) => tag.label) + ); + const updatedTagsToRemove = tagsToRemove.concat( + labels + .filter((tag) => tag.checked !== 'on' && selectedTags.includes(tag.label)) + .map((tag) => tag.label) + ); + updateTagsHook.bulkUpdateTags( agents!, - tagsToAdd, - tagsToRemove, + updatedTagsToAdd, + updatedTagsToRemove, (hasCompleted) => handleTagsUpdated(tagsToAdd, tagsToRemove, hasCompleted), successMessage, errorMessage diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index 4a6c772b69b9..17c745bfd285 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -134,6 +134,7 @@ export async function bulkCreateAgentActionResults( await esClient.bulk({ index: AGENT_ACTIONS_RESULTS_INDEX, body: bulkBody, + refresh: 'wait_for', }); } diff --git a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts index 1283c9433ebb..2dd546511dd9 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts @@ -121,9 +121,18 @@ export default function (providerContext: FtrProviderContext) { ]); expect(agent2data.body.item.policy_id).to.eql('policy2'); expect(agent3data.body.item.policy_id).to.eql('policy2'); + + const { body } = await supertest + .get(`/api/fleet/agents/action_status`) + .set('kbn-xsrf', 'xxx'); + const actionStatus = body.items[0]; + + expect(actionStatus.status).to.eql('FAILED'); + expect(actionStatus.nbAgentsActionCreated).to.eql(2); + expect(actionStatus.nbAgentsFailed).to.eql(3); }); - it('should allow to reassign multiple agents by id -- mixed invalid, hosted, etc', async () => { + it('should return error when none of the agents can be reassigned -- mixed invalid, hosted, etc', async () => { // agent1 is enrolled in policy1. set policy1 to hosted await supertest .put(`/api/fleet/agent_policies/policy1`) @@ -131,13 +140,15 @@ export default function (providerContext: FtrProviderContext) { .send({ name: 'Test policy', namespace: 'default', is_managed: true }) .expect(200); - await supertest + const { body } = await supertest .post(`/api/fleet/agents/bulk_reassign`) .set('kbn-xsrf', 'xxx') .send({ agents: ['agent2', 'INVALID_ID', 'agent3'], policy_id: 'policy2', - }); + }) + .expect(400); + expect(body.message).to.eql('No agents to reassign, already assigned or hosted agents'); const [agent2data, agent3data] = await Promise.all([ supertest.get(`/api/fleet/agents/agent2`), diff --git a/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts b/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts index fe31806038a5..7f778d77f1a5 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts @@ -140,6 +140,13 @@ export default function (providerContext: FtrProviderContext) { expect(typeof agent3data.body.item.unenrollment_started_at).to.be('undefined'); expect(typeof agent3data.body.item.unenrolled_at).to.be('undefined'); expect(agent2data.body.item.active).to.eql(true); + + const { body } = await supertest + .get(`/api/fleet/agents/action_status`) + .set('kbn-xsrf', 'xxx'); + const actionStatus = body.items[0]; + expect(actionStatus.status).to.eql('FAILED'); + expect(actionStatus.nbAgentsFailed).to.eql(2); }); it('/agents/bulk_unenroll should allow to unenroll multiple agents by id from an regular agent policy', async () => { diff --git a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts index 88079ae5e1af..ff11076addce 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts @@ -88,27 +88,46 @@ export default function (providerContext: FtrProviderContext) { }); it('should bulk update tags of multiple agents by kuery in batches', async () => { - await supertest + const { body: actionBody } = await supertest .post(`/api/fleet/agents/bulk_update_agent_tags`) .set('kbn-xsrf', 'xxx') .send({ agents: 'active: true', tagsToAdd: ['newTag'], tagsToRemove: ['existingTag'], - batchSize: 2, + batchSize: 3, }) .expect(200); + const actionId = actionBody.actionId; + + const verifyActionResult = async () => { + const { body } = await supertest.get(`/api/fleet/agents`).set('kbn-xsrf', 'xxx'); + expect(body.total).to.eql(4); + body.items.forEach((agent: any) => { + expect(agent.tags.includes('newTag')).to.be(true); + expect(agent.tags.includes('existingTag')).to.be(false); + }); + }; + await new Promise((resolve, reject) => { - setTimeout(async () => { - const { body } = await supertest.get(`/api/fleet/agents`).set('kbn-xsrf', 'xxx'); - expect(body.total).to.eql(4); - body.items.forEach((agent: any) => { - expect(agent.tags.includes('newTag')).to.be(true); - expect(agent.tags.includes('existingTag')).to.be(false); - }); - resolve({}); - }, 2000); + let attempts = 0; + const intervalId = setInterval(async () => { + if (attempts > 4) { + clearInterval(intervalId); + reject('action timed out'); + } + ++attempts; + const { + body: { items: actionStatuses }, + } = await supertest.get(`/api/fleet/agents/action_status`).set('kbn-xsrf', 'xxx'); + const action = actionStatuses.find((a: any) => a.actionId === actionId); + if (action && action.nbAgentsAck === 4) { + clearInterval(intervalId); + await verifyActionResult(); + resolve({}); + } + }, 1000); }).catch((e) => { throw e; }); @@ -156,6 +175,13 @@ export default function (providerContext: FtrProviderContext) { expect(agent1data.body.item.tags.includes('newTag')).to.be(false); expect(agent2data.body.item.tags.includes('newTag')).to.be(true); + + const { body } = await supertest + .get(`/api/fleet/agents/action_status`) + .set('kbn-xsrf', 'xxx'); + const actionStatus = body.items[0]; + expect(actionStatus.status).to.eql('FAILED'); + expect(actionStatus.nbAgentsFailed).to.eql(1); }); }); }); diff --git a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts index e2762e21f33a..e8dad8624021 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts @@ -608,7 +608,7 @@ export default function (providerContext: FtrProviderContext) { .send({ agents: 'active:true', version: fleetServerVersion, - batchSize: 2, + batchSize: 3, }) .expect(200); @@ -626,7 +626,7 @@ export default function (providerContext: FtrProviderContext) { await new Promise((resolve, reject) => { let attempts = 0; const intervalId = setInterval(async () => { - if (attempts > 2) { + if (attempts > 4) { clearInterval(intervalId); reject('action timed out'); } @@ -636,7 +636,7 @@ export default function (providerContext: FtrProviderContext) { } = await supertest.get(`/api/fleet/agents/action_status`).set('kbn-xsrf', 'xxx'); const action = actionStatuses.find((a: any) => a.actionId === actionId); // 2 upgradeable - if (action && action.nbAgentsActionCreated === 2) { + if (action && action.nbAgentsActionCreated === 2 && action.nbAgentsFailed === 3) { clearInterval(intervalId); await verifyActionResult(); resolve({}); @@ -1032,6 +1032,13 @@ export default function (providerContext: FtrProviderContext) { expect(typeof agent1data.body.item.upgrade_started_at).to.be('undefined'); expect(typeof agent2data.body.item.upgrade_started_at).to.be('string'); + + const { body } = await supertest + .get(`/api/fleet/agents/action_status`) + .set('kbn-xsrf', 'xxx'); + const actionStatus = body.items[0]; + expect(actionStatus.status).to.eql('FAILED'); + expect(actionStatus.nbAgentsFailed).to.eql(1); }); it('enrolled in a hosted agent policy bulk upgrade with force flag should respond with 200 and update the agent SOs', async () => { From 7d69aae2696696342382b5f66267826dd9967cb6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 28 Sep 2022 09:54:33 +0100 Subject: [PATCH 052/185] [ML] Fixing case of log pattern analysis title (#142034) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/ml/public/application/aiops/log_categorization.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx index e1d816d61357..899006b5918d 100644 --- a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx +++ b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx @@ -34,7 +34,7 @@ export const LogCategorizationPage: FC = () => { From 8f793cb83c167da32d696715e5b1f1a65f8c1c6c Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 28 Sep 2022 12:39:26 +0300 Subject: [PATCH 053/185] [TSVB][Lens] Navigate to Lens TSVB Metric (#140878) * Added tsvb metric converting to Lens. * Allowed to convert to lens with invalid color rules. * Fixed static value converting. * Fixed tests. * Added unit tests for getPalette. * Added functional tests for converting to Lens. Co-authored-by: Yaroslav Kuznietsov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/convert_to_lens/index.test.ts | 2 +- .../public/convert_to_lens/index.ts | 4 + .../lib/configurations/metric/index.ts | 67 +++++ .../lib/configurations/metric/palette.test.ts | 177 ++++++++++++ .../lib/configurations/metric/palette.ts | 214 ++++++++++++++ .../convert_to_lens/lib/convert/index.ts | 2 +- .../lib/convert/static_value.ts | 22 +- .../convert_to_lens/lib/convert/types.ts | 2 +- .../lib/metrics/supported_metrics.ts | 7 +- .../lib/series/metrics_columns.test.ts | 14 +- .../lib/series/metrics_columns.ts | 16 +- .../convert_to_lens/metric/index.test.ts | 272 ++++++++++++++++++ .../public/convert_to_lens/metric/index.ts | 130 +++++++++ .../convert_to_lens/metric/utils.test.ts | 104 +++++++ .../public/convert_to_lens/metric/utils.ts | 65 +++++ .../convert_to_lens/timeseries/index.ts | 4 +- .../public/convert_to_lens/top_n/index.ts | 4 +- .../public/convert_to_lens/types.ts | 8 +- .../convert_to_lens/types/configurations.ts | 24 +- .../visualizations/metric/suggestions.test.ts | 8 + .../visualizations/metric/suggestions.ts | 2 +- .../visualizations/metric/visualization.tsx | 40 ++- .../apps/lens/group3/tsvb_open_in_lens.ts | 13 +- 23 files changed, 1179 insertions(+), 22 deletions(-) create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts create mode 100644 src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts index 435335fe9dd2..309f066b18f2 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts @@ -40,7 +40,7 @@ describe('convertTSVBtoLensConfiguration', () => { test('should return null for a not supported chart', async () => { const metricModel = { ...model, - type: 'metric', + type: 'markdown', } as Panel; const triggerOptions = await convertTSVBtoLensConfiguration(metricModel); expect(triggerOptions).toBeNull(); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts index 5b92c0ab2166..a64118a1cb50 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts @@ -21,6 +21,10 @@ const getConvertFnByType = (type: PANEL_TYPES) => { const { convertToLens } = await import('./top_n'); return convertToLens; }, + [PANEL_TYPES.METRIC]: async () => { + const { convertToLens } = await import('./metric'); + return convertToLens; + }, }; return convertionFns[type]?.(); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts new file mode 100644 index 000000000000..d1f24485d764 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { MetricVisConfiguration } from '@kbn/visualizations-plugin/common'; +import { Metric, Panel, Series } from '../../../../../common/types'; +import { Column, Layer } from '../../convert'; +import { getSeriesAgg } from '../../series'; +import { getPalette } from './palette'; + +const getMetricWithCollapseFn = (series: Series | undefined) => { + if (!series) { + return; + } + const { metrics, seriesAgg } = getSeriesAgg(series.metrics); + const visibleMetric = metrics[metrics.length - 1]; + return { metric: visibleMetric, collapseFn: seriesAgg }; +}; + +const findMetricColumn = (metric: Metric | undefined, columns: Column[]) => { + if (!metric) { + return; + } + + return columns.find((column) => 'meta' in column && column.meta.metricId === metric.id); +}; + +export const getConfigurationForMetric = ( + model: Panel, + layer: Layer, + bucket?: Column +): MetricVisConfiguration | null => { + const [primarySeries, secondarySeries] = model.series.filter(({ hidden }) => !hidden); + + const primaryMetricWithCollapseFn = getMetricWithCollapseFn(primarySeries); + + if (!primaryMetricWithCollapseFn || !primaryMetricWithCollapseFn.metric) { + return null; + } + + const secondaryMetricWithCollapseFn = getMetricWithCollapseFn(secondarySeries); + const primaryColumn = findMetricColumn(primaryMetricWithCollapseFn.metric, layer.columns); + const secondaryColumn = findMetricColumn(secondaryMetricWithCollapseFn?.metric, layer.columns); + + if (primaryMetricWithCollapseFn.collapseFn && secondaryMetricWithCollapseFn?.collapseFn) { + return null; + } + + const palette = getPalette(model.background_color_rules ?? []); + if (palette === null) { + return null; + } + + return { + layerId: layer.layerId, + layerType: 'data', + metricAccessor: primaryColumn?.columnId, + secondaryMetricAccessor: secondaryColumn?.columnId, + breakdownByAccessor: bucket?.columnId, + palette, + collapseFn: primaryMetricWithCollapseFn.collapseFn ?? secondaryMetricWithCollapseFn?.collapseFn, + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts new file mode 100644 index 000000000000..827dc15ff171 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getPalette } from './palette'; + +describe('getPalette', () => { + const invalidRules = [ + { id: 'some-id-0' }, + { id: 'some-id-1', value: 10 }, + { id: 'some-id-2', operator: 'gte' }, + { id: 'some-id-3', color: '#000' }, + { id: 'some-id-4', background_color: '#000' }, + ]; + test('should return undefined if no filled rules was provided', () => { + expect(getPalette([])).toBeUndefined(); + expect(getPalette(invalidRules)).toBeUndefined(); + }); + + test('should return undefined if only one valid rule is provided and it is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gt', value: 100, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); + + test('should return custom palette if only one valid rule is provided and it is lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + ]) + ).toEqual({ + name: 'custom', + params: { + colorStops: [{ color: '#000000', stop: 100 }], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 100, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 1, + stops: [{ color: '#000000', stop: 100 }], + }, + type: 'palette', + }); + }); + + test('should return undefined if more than two types of rules', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); + + test('should return undefined if two types of rules and last rule is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lt', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); + + test('should return undefined if all rules are lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lte', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); + + test('should return undefined if two types of rules and all except last one are lt and last one is not lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'gte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, + ]) + ).toBeUndefined(); + }); + + test('should return custom palette if two types of rules and all except last one is lt and last one is lte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'lt', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'lt', value: 150, background_color: '#000' }, + ]) + ).toEqual({ + name: 'custom', + params: { + colorStops: [ + { color: '#000000', stop: -Infinity }, + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'below', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + steps: 4, + stops: [ + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); + }); + + test('should return custom palette if last one is lte and all previous are gte', () => { + expect(getPalette([])).toBeUndefined(); + expect( + getPalette([ + ...invalidRules, + { id: 'some-id-5', operator: 'gte', value: 100, background_color: '#000' }, + { id: 'some-id-7', operator: 'lte', value: 200, background_color: '#000' }, + { id: 'some-id-6', operator: 'gte', value: 150, background_color: '#000' }, + ]) + ).toEqual({ + name: 'custom', + params: { + colorStops: [ + { color: '#000000', stop: 100 }, + { color: '#000000', stop: 150 }, + ], + continuity: 'none', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: 200, + rangeMin: 100, + rangeType: 'number', + reverse: false, + steps: 2, + stops: [ + { color: '#000000', stop: 150 }, + { color: '#000000', stop: 200 }, + ], + }, + type: 'palette', + }); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts new file mode 100644 index 000000000000..55741c57595e --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import color from 'color'; +import { ColorStop, CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; +import { uniqBy } from 'lodash'; +import { Panel } from '../../../../../common/types'; + +const Operators = { + GTE: 'gte', + GT: 'gt', + LTE: 'lte', + LT: 'lt', +} as const; + +type ColorStopsWithMinMax = Pick< + CustomPaletteParams, + 'colorStops' | 'stops' | 'steps' | 'rangeMax' | 'rangeMin' | 'continuity' +>; + +const getColorStopsWithMinMaxForAllGteOrWithLte = ( + rules: Exclude, + tailOperator: string +): ColorStopsWithMinMax => { + const lastRule = rules[rules.length - 1]; + const lastRuleColor = (lastRule.background_color ?? lastRule.color)!; + + const colorStops = rules.reduce((colors, rule, index, rulesArr) => { + const rgbColor = (rule.background_color ?? rule.color)!; + if (index === rulesArr.length - 1 && tailOperator === Operators.LTE) { + return colors; + } + // if last operation is LTE, color of gte should be replaced by lte + if (index === rulesArr.length - 2 && tailOperator === Operators.LTE) { + return [ + ...colors, + { + color: color(lastRuleColor).hex(), + stop: rule.value!, + }, + ]; + } + return [ + ...colors, + { + color: color(rgbColor).hex(), + stop: rule.value!, + }, + ]; + }, []); + + const stops = colorStops.reduce((prevStops, colorStop, index, colorStopsArr) => { + if (index === colorStopsArr.length - 1) { + return [ + ...prevStops, + { + color: colorStop.color, + stop: tailOperator === Operators.LTE ? lastRule.value! : colorStop.stop + 1, + }, + ]; + } + return [...prevStops, { color: colorStop.color, stop: colorStopsArr[index + 1].stop }]; + }, []); + + const [rule] = rules; + return { + rangeMin: rule.value, + rangeMax: tailOperator === Operators.LTE ? lastRule.value : Infinity, + colorStops, + stops, + steps: colorStops.length, + continuity: tailOperator === Operators.LTE ? 'none' : 'above', + }; +}; + +const getColorStopsWithMinMaxForLtWithLte = ( + rules: Exclude +): ColorStopsWithMinMax => { + const lastRule = rules[rules.length - 1]; + const colorStops = rules.reduce((colors, rule, index, rulesArr) => { + if (index === 0) { + return [{ color: color((rule.background_color ?? rule.color)!).hex(), stop: -Infinity }]; + } + const rgbColor = (rule.background_color ?? rule.color)!; + return [ + ...colors, + { + color: color(rgbColor).hex(), + stop: rulesArr[index - 1].value!, + }, + ]; + }, []); + + const stops = colorStops.reduce((prevStops, colorStop, index, colorStopsArr) => { + if (index === colorStopsArr.length - 1) { + return [ + ...prevStops, + { + color: colorStop.color, + stop: lastRule.value!, + }, + ]; + } + return [...prevStops, { color: colorStop.color, stop: colorStopsArr[index + 1].stop }]; + }, []); + + return { + rangeMin: -Infinity, + rangeMax: lastRule.value, + colorStops, + stops, + steps: colorStops.length + 1, + continuity: 'below', + }; +}; + +const getColorStopWithMinMaxForLte = ( + rule: Exclude[number] +): ColorStopsWithMinMax => { + const colorStop = { + color: color((rule.background_color ?? rule.color)!).hex(), + stop: rule.value!, + }; + return { + rangeMin: -Infinity, + rangeMax: rule.value!, + colorStops: [colorStop], + stops: [colorStop], + steps: 1, + continuity: 'below', + }; +}; + +const getCustomPalette = ( + colorStopsWithMinMax: ColorStopsWithMinMax +): PaletteOutput => { + return { + name: 'custom', + params: { + continuity: 'all', + maxSteps: 5, + name: 'custom', + progression: 'fixed', + rangeMax: Infinity, + rangeMin: -Infinity, + rangeType: 'number', + reverse: false, + ...colorStopsWithMinMax, + }, + type: 'palette', + }; +}; + +export const getPalette = ( + rules: Exclude +): PaletteOutput | null | undefined => { + const validRules = + rules.filter( + ({ operator, color: textColor, value, background_color: bColor }) => + operator && (bColor ?? textColor) && value !== undefined + ) ?? []; + + validRules.sort((rule1, rule2) => { + return rule1.value! - rule2.value!; + }); + + const kindOfRules = uniqBy(validRules, 'operator'); + + if (!kindOfRules.length) { + return; + } + + // lnsMetric is supporting lte only, if one rule is defined + if (validRules.length === 1) { + if (validRules[0].operator !== Operators.LTE) { + return; + } + return getCustomPalette(getColorStopWithMinMaxForLte(validRules[0])); + } + + const headRules = validRules.slice(0, -1); + const tailRule = validRules[validRules.length - 1]; + const kindOfHeadRules = uniqBy(headRules, 'operator'); + + if ( + kindOfHeadRules.length > 1 || + (kindOfHeadRules[0].operator !== tailRule.operator && tailRule.operator !== Operators.LTE) + ) { + return; + } + + const [rule] = kindOfHeadRules; + + if (rule.operator === Operators.LTE) { + return; + } + + if (rule.operator === Operators.LT) { + if (tailRule.operator !== Operators.LTE) { + return; + } + return getCustomPalette(getColorStopsWithMinMaxForLtWithLte(validRules)); + } + + if (rule.operator === Operators.GTE) { + return getCustomPalette( + getColorStopsWithMinMaxForAllGteOrWithLte(validRules, tailRule.operator!) + ); + } +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts index 36f05c440bdc..e03701e6ea15 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/index.ts @@ -17,7 +17,7 @@ export { export { convertToCumulativeSumColumns } from './cumulative_sum'; export { convertFilterRatioToFormulaColumn } from './filter_ratio'; export { convertToLastValueColumn } from './last_value'; -export { convertToStaticValueColumn } from './static_value'; +export { convertToStaticValueColumn, convertStaticValueToFormulaColumn } from './static_value'; export { convertToFiltersColumn } from './filters'; export { convertToDateHistogramColumn } from './date_histogram'; export { convertToTermsColumn } from './terms'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts index c4400f72b289..e03a9d782136 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/static_value.ts @@ -7,9 +7,10 @@ */ import { StaticValueParams } from '@kbn/visualizations-plugin/common/convert_to_lens'; -import { CommonColumnsConverterArgs, StaticValueColumn } from './types'; +import { CommonColumnsConverterArgs, FormulaColumn, StaticValueColumn } from './types'; import type { Metric } from '../../../../common/types'; import { createColumn, getFormat } from './column'; +import { createFormulaColumn } from './formula'; export const convertToStaticValueParams = ({ value }: Metric): StaticValueParams => ({ value, @@ -37,3 +38,22 @@ export const convertToStaticValueColumn = ( }, }; }; + +export const convertStaticValueToFormulaColumn = ( + { series, metrics, dataView }: CommonColumnsConverterArgs, + { + visibleSeriesCount = 0, + reducedTimeRange, + }: { visibleSeriesCount?: number; reducedTimeRange?: string } = {} +): FormulaColumn | null => { + // Lens support reference lines only when at least one layer data exists + if (visibleSeriesCount === 1) { + return null; + } + const currentMetric = metrics[metrics.length - 1]; + return createFormulaColumn(currentMetric.value ?? '', { + series, + metric: currentMetric, + dataView, + }); +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts index d21291dc2622..e5b862a0fe70 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts @@ -85,7 +85,7 @@ export type MovingAverageColumn = GenericColumnWithMeta; export type StaticValueColumn = GenericColumnWithMeta; -type ColumnsWithoutMeta = FiltersColumn | TermsColumn | DateHistogramColumn; +export type ColumnsWithoutMeta = FiltersColumn | TermsColumn | DateHistogramColumn; export type AnyColumnWithReferences = GenericColumnWithMeta; type CommonColumns = Exclude; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts index 933e7e344b7f..76d15793f451 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts @@ -62,7 +62,12 @@ export type SupportedMetrics = LocalSupportedMetrics & { [Key in UnsupportedSupportedMetrics]?: null; }; -const supportedPanelTypes: readonly PANEL_TYPES[] = [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N]; +const supportedPanelTypes: readonly PANEL_TYPES[] = [ + PANEL_TYPES.TIMESERIES, + PANEL_TYPES.TOP_N, + PANEL_TYPES.METRIC, +]; + const supportedTimeRangeModes: readonly TIME_RANGE_DATA_MODES[] = [ TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, TIME_RANGE_DATA_MODES.LAST_VALUE, diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.test.ts index 5ca1fe71a0ad..4461072c8df6 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.test.ts @@ -20,6 +20,7 @@ const mockConvertToCounterRateColumn = jest.fn(); const mockConvertOtherAggsToFormulaColumn = jest.fn(); const mockConvertToLastValueColumn = jest.fn(); const mockConvertToStaticValueColumn = jest.fn(); +const mockConvertStaticValueToFormulaColumn = jest.fn(); const mockConvertToStandartDeviationColumn = jest.fn(); const mockConvertMetricAggregationColumnWithoutSpecialParams = jest.fn(); @@ -32,6 +33,7 @@ jest.mock('../convert', () => ({ convertOtherAggsToFormulaColumn: jest.fn(() => mockConvertOtherAggsToFormulaColumn()), convertToLastValueColumn: jest.fn(() => mockConvertToLastValueColumn()), convertToStaticValueColumn: jest.fn(() => mockConvertToStaticValueColumn()), + convertStaticValueToFormulaColumn: jest.fn(() => mockConvertStaticValueToFormulaColumn()), convertToStandartDeviationColumn: jest.fn(() => mockConvertToStandartDeviationColumn()), convertMetricAggregationColumnWithoutSpecialParams: jest.fn(() => mockConvertMetricAggregationColumnWithoutSpecialParams() @@ -138,8 +140,18 @@ describe('getMetricsColumns', () => { mockConvertToLastValueColumn, ], [ - 'call convertToStaticValueColumn if metric type is static', + 'call convertStaticValueToFormulaColumn if metric type is static', [createSeries({ metrics: [{ type: TSVB_METRIC_TYPES.STATIC, id: '1' }] }), dataView, 1], + mockConvertStaticValueToFormulaColumn, + ], + [ + 'call convertToStaticValueColumn if metric type is static and isStaticValueColumnSupported is true', + [ + createSeries({ metrics: [{ type: TSVB_METRIC_TYPES.STATIC, id: '1' }] }), + dataView, + 1, + { isStaticValueColumnSupported: true }, + ], mockConvertToStaticValueColumn, ], [ diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.ts index 07294a3a61aa..8f7d4ded0d07 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/metrics_columns.ts @@ -21,6 +21,7 @@ import { convertFilterRatioToFormulaColumn, convertToLastValueColumn, convertToStaticValueColumn, + convertStaticValueToFormulaColumn, convertMetricAggregationColumnWithoutSpecialParams, convertToCounterRateColumn, convertToStandartDeviationColumn, @@ -31,7 +32,10 @@ export const getMetricsColumns = ( series: Series, dataView: DataView, visibleSeriesCount: number, - reducedTimeRange?: string + { + isStaticValueColumnSupported = false, + reducedTimeRange, + }: { reducedTimeRange?: string; isStaticValueColumnSupported?: boolean } = {} ): Column[] | null => { const { metrics: validMetrics, seriesAgg } = getSeriesAgg( series.metrics as [Metric, ...Metric[]] @@ -117,10 +121,12 @@ export const getMetricsColumns = ( return getValidColumns(column); } case 'static': { - const column = convertToStaticValueColumn(columnsConverterArgs, { - visibleSeriesCount, - reducedTimeRange, - }); + const column = isStaticValueColumnSupported + ? convertToStaticValueColumn(columnsConverterArgs, { + visibleSeriesCount, + reducedTimeRange, + }) + : convertStaticValueToFormulaColumn(columnsConverterArgs); return getValidColumns(column); } case 'std_deviation': { diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts new file mode 100644 index 000000000000..9407599573d9 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts @@ -0,0 +1,272 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/public'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { convertToLens } from '.'; +import { createPanel, createSeries } from '../lib/__mocks__'; + +const mockGetMetricsColumns = jest.fn(); +const mockGetBucketsColumns = jest.fn(); +const mockGetConfigurationForMetric = jest.fn(); +const mockIsValidMetrics = jest.fn(); +const mockGetDatasourceValue = jest + .fn() + .mockImplementation(() => Promise.resolve(stubLogstashDataView)); +const mockGetDataSourceInfo = jest.fn(); + +jest.mock('../../services', () => ({ + getDataViewsStart: jest.fn(() => mockGetDatasourceValue), +})); + +jest.mock('../lib/series', () => ({ + getMetricsColumns: jest.fn(() => mockGetMetricsColumns()), + getBucketsColumns: jest.fn(() => mockGetBucketsColumns()), +})); + +jest.mock('../lib/configurations/metric', () => ({ + getConfigurationForMetric: jest.fn(() => mockGetConfigurationForMetric()), +})); + +jest.mock('../lib/metrics', () => ({ + isValidMetrics: jest.fn(() => mockIsValidMetrics()), + getReducedTimeRange: jest.fn().mockReturnValue('10'), +})); + +jest.mock('../lib/datasource', () => ({ + getDataSourceInfo: jest.fn(() => mockGetDataSourceInfo()), +})); + +describe('convertToLens', () => { + const model = createPanel({ + series: [ + createSeries({ + metrics: [ + { id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }, + { id: 'some-id-1', type: METRIC_TYPES.COUNT }, + ], + }), + ], + }); + + const bucket = { + isBucketed: true, + isSplit: true, + operationType: 'terms', + params: { + exclude: [], + excludeIsRegex: true, + include: [], + includeIsRegex: true, + orderAgg: { + columnId: 'some-id-0', + dataType: 'number', + isBucketed: true, + isSplit: false, + operationType: 'average', + params: {}, + sourceField: 'bytes', + }, + orderBy: { columnId: 'some-id-0', type: 'column' }, + orderDirection: 'asc', + otherBucket: false, + parentFormat: { id: 'terms' }, + secondaryFields: [], + size: 3, + }, + sourceField: 'bytes', + }; + + const bucket2 = { + isBucketed: true, + isSplit: true, + operationType: 'terms', + params: { + exclude: [], + excludeIsRegex: true, + include: [], + includeIsRegex: true, + orderAgg: { + columnId: 'some-id-1', + dataType: 'number', + isBucketed: true, + isSplit: false, + operationType: 'average', + params: {}, + sourceField: 'bytes', + }, + orderBy: { columnId: 'some-id-1', type: 'column' }, + orderDirection: 'desc', + otherBucket: false, + parentFormat: { id: 'terms' }, + secondaryFields: [], + size: 10, + }, + sourceField: 'bytes', + }; + + const metric = { + meta: { metricId: 'some-id-0' }, + operationType: 'last_value', + params: { showArrayValues: false, sortField: '@timestamp' }, + reducedTimeRange: '10m', + }; + + beforeEach(() => { + mockIsValidMetrics.mockReturnValue(true); + mockGetDataSourceInfo.mockReturnValue({ + indexPatternId: 'test-index-pattern', + timeField: 'timeField', + indexPattern: { id: 'test-index-pattern' }, + }); + mockGetMetricsColumns.mockReturnValue([{}]); + mockGetBucketsColumns.mockReturnValue([{}]); + mockGetConfigurationForMetric.mockReturnValue({}); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should return null for invalid metrics', async () => { + mockIsValidMetrics.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockIsValidMetrics).toBeCalledTimes(1); + }); + + test('should return null for invalid or unsupported metrics', async () => { + mockGetMetricsColumns.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockGetMetricsColumns).toBeCalledTimes(1); + }); + + test('should return null for invalid or unsupported buckets', async () => { + mockGetBucketsColumns.mockReturnValue(null); + const result = await convertToLens(model); + expect(result).toBeNull(); + expect(mockGetBucketsColumns).toBeCalledTimes(1); + }); + + test('should return state for valid model', async () => { + const result = await convertToLens(model); + expect(result).toBeDefined(); + expect(result?.type).toBe('lnsMetric'); + expect(mockGetBucketsColumns).toBeCalledTimes(model.series.length); + expect(mockGetConfigurationForMetric).toBeCalledTimes(1); + }); + + test('should skip hidden series', async () => { + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: true, + }), + ], + }) + ); + expect(result).toBeDefined(); + expect(result?.type).toBe('lnsMetric'); + expect(mockIsValidMetrics).toBeCalledTimes(0); + }); + + test('should return null if multiple indexPatterns are provided', async () => { + mockGetDataSourceInfo.mockReturnValueOnce({ + indexPatternId: 'test-index-pattern-1', + timeField: 'timeField', + indexPattern: { id: 'test-index-pattern-1' }, + }); + + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + ], + }) + ); + expect(result).toBeNull(); + }); + + test('should return null if visible series is 2 and bucket is 1', async () => { + mockGetBucketsColumns.mockReturnValueOnce([bucket]); + mockGetBucketsColumns.mockReturnValueOnce([]); + mockGetMetricsColumns.mockReturnValueOnce([metric]); + + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + ], + }) + ); + expect(result).toBeNull(); + }); + + test('should return null if visible series is 2 and two not unique buckets', async () => { + mockGetBucketsColumns.mockReturnValueOnce([bucket]); + mockGetBucketsColumns.mockReturnValueOnce([bucket2]); + mockGetMetricsColumns.mockReturnValueOnce([metric]); + + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + ], + }) + ); + expect(result).toBeNull(); + }); + + test('should return state if visible series is 2 and two unique buckets', async () => { + mockGetBucketsColumns.mockReturnValueOnce([bucket]); + mockGetBucketsColumns.mockReturnValueOnce([bucket]); + mockGetMetricsColumns.mockReturnValueOnce([metric]); + + const result = await convertToLens( + createPanel({ + series: [ + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + createSeries({ + metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }], + hidden: false, + }), + ], + }) + ); + expect(result).toBeDefined(); + expect(result?.type).toBe('lnsMetric'); + expect(mockGetConfigurationForMetric).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts new file mode 100644 index 000000000000..25f55b5a1c44 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import uuid from 'uuid'; +import { DataView, parseTimeShift } from '@kbn/data-plugin/common'; +import { getIndexPatternIds } from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { PANEL_TYPES } from '../../../common/enums'; +import { getDataViewsStart } from '../../services'; +import { getDataSourceInfo } from '../lib/datasource'; +import { getMetricsColumns, getBucketsColumns } from '../lib/series'; +import { getConfigurationForMetric as getConfiguration } from '../lib/configurations/metric'; +import { getReducedTimeRange, isValidMetrics } from '../lib/metrics'; +import { ConvertTsvbToLensVisualization } from '../types'; +import { ColumnsWithoutMeta, Layer as ExtendedLayer } from '../lib/convert'; +import { excludeMetaFromLayers, getUniqueBuckets } from './utils'; + +const MAX_SERIES = 2; +const MAX_BUCKETS = 2; + +export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => { + const dataViews = getDataViewsStart(); + const seriesNum = model.series.filter((series) => !series.hidden).length; + + const indexPatternIds = new Set(); + const visibleSeries = model.series.filter(({ hidden }) => !hidden); + let currentIndexPattern: DataView | null = null; + for (const series of visibleSeries) { + const datasourceInfo = await getDataSourceInfo( + model.index_pattern, + model.time_field, + Boolean(series.override_index_pattern), + series.series_index_pattern, + series.series_time_field, + dataViews + ); + + if (!datasourceInfo) { + return null; + } + + const { indexPatternId, indexPattern } = datasourceInfo; + indexPatternIds.add(indexPatternId); + currentIndexPattern = indexPattern; + } + + if (indexPatternIds.size > 1) { + return null; + } + + const [indexPatternId] = indexPatternIds.values(); + + const buckets = []; + const metrics = []; + + // handle multiple layers/series + for (const series of visibleSeries) { + // not valid time shift + if (series.offset_time && parseTimeShift(series.offset_time) === 'invalid') { + return null; + } + + if (!isValidMetrics(series.metrics, PANEL_TYPES.METRIC, series.time_range_mode)) { + return null; + } + + const reducedTimeRange = getReducedTimeRange(model, series, timeRange); + + // handle multiple metrics + const metricsColumns = getMetricsColumns(series, currentIndexPattern!, seriesNum, { + reducedTimeRange, + }); + if (metricsColumns === null) { + return null; + } + + const bucketsColumns = getBucketsColumns( + model, + series, + metricsColumns, + currentIndexPattern!, + false + ); + + if (bucketsColumns === null) { + return null; + } + + buckets.push(...bucketsColumns); + metrics.push(...metricsColumns); + } + + let uniqueBuckets = buckets; + if (visibleSeries.length === MAX_SERIES && buckets.length) { + if (buckets.length !== MAX_BUCKETS) { + return null; + } + + uniqueBuckets = getUniqueBuckets(buckets as ColumnsWithoutMeta[]); + if (uniqueBuckets.length !== 1) { + return null; + } + } + + const [bucket] = uniqueBuckets; + + const extendedLayer: ExtendedLayer = { + indexPatternId: indexPatternId as string, + layerId: uuid(), + columns: [...metrics, ...(bucket ? [bucket] : [])], + columnOrder: [], + }; + + const configuration = getConfiguration(model, extendedLayer, bucket); + if (!configuration) { + return null; + } + + const layers = Object.values(excludeMetaFromLayers({ 0: extendedLayer })); + return { + type: 'lnsMetric', + layers, + configuration, + indexPatternIds: getIndexPatternIds(layers), + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts new file mode 100644 index 000000000000..8f880dfe95c5 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Column, DateHistogramColumn, TermsColumn } from '../lib/convert'; +import { getUniqueBuckets } from './utils'; + +describe('getUniqueBuckets', () => { + const bucket2: TermsColumn = { + columnId: '12', + dataType: 'string', + isBucketed: true, + isSplit: true, + operationType: 'terms', + params: { + exclude: [], + excludeIsRegex: true, + include: [], + includeIsRegex: true, + orderAgg: { + columnId: 'some-id-1', + dataType: 'number', + isBucketed: true, + isSplit: false, + operationType: 'average', + params: {}, + sourceField: 'bytes', + }, + orderBy: { columnId: 'some-id-1', type: 'column' }, + orderDirection: 'desc', + otherBucket: false, + parentFormat: { id: 'terms' }, + secondaryFields: [], + size: 10, + }, + sourceField: 'bytes', + }; + + it('should return unique buckets', () => { + expect(getUniqueBuckets([bucket2, bucket2])).toEqual([bucket2]); + }); + + it('should ignore columnIds', () => { + const bucketWithOtherColumnIds = { + ...bucket2, + columnId: '22', + params: { + ...bucket2.params, + orderAgg: { ...bucket2.params.orderAgg, columnId: '---1' } as Column, + orderBy: { ...bucket2.params.orderBy, columnId: '---2' } as { + type: 'column'; + columnId: string; + }, + }, + }; + expect(getUniqueBuckets([bucket2, bucketWithOtherColumnIds])).toEqual([bucket2]); + }); + + it('should respect differences of terms', () => { + const bucketWithOtherColumnIds = { + ...bucket2, + columnId: '22', + params: { + ...bucket2.params, + orderAgg: { ...bucket2.params.orderAgg, columnId: '---1' } as Column, + orderBy: { ...bucket2.params.orderBy, columnId: '---2' } as { + type: 'column'; + columnId: string; + }, + }, + sourceField: 'name', + }; + expect(getUniqueBuckets([bucket2, bucketWithOtherColumnIds])).toEqual([ + bucket2, + bucketWithOtherColumnIds, + ]); + }); + + it('should respect differences of other buckets', () => { + const bucket: DateHistogramColumn = { + dataType: 'number', + isBucketed: true, + isSplit: false, + operationType: 'date_histogram', + params: { dropPartials: false, includeEmptyRows: true, interval: 'auto' }, + sourceField: 'field1', + columnId: '1', + }; + const bucket1 = { + ...bucket, + columnId: '22', + }; + expect(getUniqueBuckets([bucket, bucket1])).toEqual([bucket]); + const bucket3 = { + ...bucket, + field: 'some-other-field', + }; + expect(getUniqueBuckets([bucket, bucket3])).toEqual([bucket, bucket3]); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts new file mode 100644 index 000000000000..8df1b0f40f8b --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/utils.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { uniqWith } from 'lodash'; +import deepEqual from 'react-fast-compare'; +import { Layer, Operations, TermsColumn } from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { Layer as ExtendedLayer, excludeMetaFromColumn, ColumnsWithoutMeta } from '../lib/convert'; + +export const excludeMetaFromLayers = ( + layers: Record +): Record => { + const newLayers: Record = {}; + Object.entries(layers).forEach(([layerId, layer]) => { + const columns = layer.columns.map(excludeMetaFromColumn); + newLayers[layerId] = { ...layer, columns }; + }); + + return newLayers; +}; + +const excludeColumnIdsFromBucket = (bucket: ColumnsWithoutMeta) => { + const { columnId, ...restBucket } = bucket; + if (bucket.operationType === Operations.TERMS) { + const { orderBy, orderAgg, ...restParams } = bucket.params; + let orderByWithoutColumn: Omit = orderBy; + if ('columnId' in orderBy) { + const { columnId: orderByColumnId, ...restOrderBy } = orderBy; + orderByWithoutColumn = restOrderBy; + } + + let orderAggWithoutColumn: Omit | undefined = + orderAgg; + if (orderAgg) { + const { columnId: cId, ...restOrderAgg } = orderAgg; + orderAggWithoutColumn = restOrderAgg; + } + + return { + ...restBucket, + params: { + ...restParams, + orderBy: orderByWithoutColumn, + orderAgg: orderAggWithoutColumn, + }, + }; + } + return restBucket; +}; + +export const getUniqueBuckets = (buckets: ColumnsWithoutMeta[]) => + uniqWith(buckets, (bucket1, bucket2) => { + if (bucket1.operationType !== bucket2.operationType) { + return false; + } + + const bucketWithoutColumnIds1 = excludeColumnIdsFromBucket(bucket1); + const bucketWithoutColumnIds2 = excludeColumnIdsFromBucket(bucket2); + + return deepEqual(bucketWithoutColumnIds1, bucketWithoutColumnIds2); + }); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts index 4610310c1b37..8cbbbf0f9e73 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts @@ -86,7 +86,9 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model: Panel return null; } // handle multiple metrics - const metricsColumns = getMetricsColumns(series, indexPattern!, seriesNum); + const metricsColumns = getMetricsColumns(series, indexPattern!, seriesNum, { + isStaticValueColumnSupported: true, + }); if (metricsColumns === null) { return null; } diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts index 630a9e5cf4c9..020aaec28f57 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts @@ -65,7 +65,9 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeR const reducedTimeRange = getReducedTimeRange(model, series, timeRange); // handle multiple metrics - const metricsColumns = getMetricsColumns(series, indexPattern!, seriesNum, reducedTimeRange); + const metricsColumns = getMetricsColumns(series, indexPattern!, seriesNum, { + reducedTimeRange, + }); if (!metricsColumns) { return null; } diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts index acee3c2a1e83..69a90c7864eb 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts @@ -6,14 +6,18 @@ * Side Public License, v 1. */ -import { NavigateToLensContext, XYConfiguration } from '@kbn/visualizations-plugin/common'; +import { + MetricVisConfiguration, + NavigateToLensContext, + XYConfiguration, +} from '@kbn/visualizations-plugin/common'; import { TimeRange } from '@kbn/data-plugin/common'; import type { Panel } from '../../common/types'; export type ConvertTsvbToLensVisualization = ( model: Panel, timeRange?: TimeRange -) => Promise | null>; +) => Promise | null>; export interface Filter { kql?: string | { [key: string]: any } | undefined; diff --git a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts index 8c348fec4c2f..fc1e440db350 100644 --- a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts +++ b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; +import { HorizontalAlignment, LayoutDirection, Position, VerticalAlignment } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; -import type { PaletteOutput } from '@kbn/coloring'; +import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import { KibanaQueryOutput } from '@kbn/data-plugin/common'; import { LegendSize } from '../../constants'; @@ -199,4 +199,22 @@ export interface TableVisConfiguration { paging?: PagingState; } -export type Configuration = XYConfiguration | TableVisConfiguration; +export interface MetricVisConfiguration { + layerId: string; + layerType: 'data'; + metricAccessor?: string; + secondaryMetricAccessor?: string; + maxAccessor?: string; + breakdownByAccessor?: string; + // the dimensions can optionally be single numbers + // computed by collapsing all rows + collapseFn?: string; + subtitle?: string; + secondaryPrefix?: string; + progressDirection?: LayoutDirection; + color?: string; + palette?: PaletteOutput; + maxCols?: number; +} + +export type Configuration = XYConfiguration | TableVisConfiguration | MetricVisConfiguration; 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 47229650e05f..45f332776a4d 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts @@ -79,6 +79,10 @@ describe('metric suggestions', () => { ...metricColumn, columnId: 'metric-column2', }, + { + ...metricColumn, + columnId: 'metric-column3', + }, ], changeType: 'unchanged', }, @@ -99,6 +103,10 @@ describe('metric suggestions', () => { ...metricColumn, columnId: 'metric-column2', }, + { + ...metricColumn, + columnId: 'metric-column3', + }, ], changeType: 'unchanged', }, diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts index ae40bf83574f..c0354d4db65e 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts @@ -11,7 +11,7 @@ import { layerTypes } from '../../../common'; import { metricLabel, MetricVisualizationState, supportedDataTypes } from './visualization'; const MAX_BUCKETED_COLUMNS = 1; -const MAX_METRIC_COLUMNS = 1; +const MAX_METRIC_COLUMNS = 2; // primary and secondary metric const hasLayerMismatch = (keptLayerIds: string[], table: TableSuggestion) => keptLayerIds.length > 1 || (keptLayerIds.length && table.layerId !== keptLayerIds[0]); diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index ed1efa900cf2..b58068b3ec20 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -19,13 +19,20 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { IconChartMetric } from '@kbn/chart-icons'; import { LayerType } from '../../../common'; import { getSuggestions } from './suggestions'; -import { Visualization, OperationMetadata, DatasourceLayers, AccessorConfig } from '../../types'; +import { + Visualization, + OperationMetadata, + DatasourceLayers, + AccessorConfig, + Suggestion, +} from '../../types'; import { layerTypes } from '../../../common'; import { GROUP_ID, LENS_METRIC_ID } from './constants'; import { DimensionEditor } from './dimension_editor'; import { Toolbar } from './toolbar'; import { generateId } from '../../id_generator'; import { FormatSelectorOptions } from '../../indexpattern_datasource/dimension_panel/format_selector'; +import { IndexPatternLayer } from '../../indexpattern_datasource/types'; export const DEFAULT_MAX_COLUMNS = 3; @@ -50,6 +57,16 @@ export interface MetricVisualizationState { maxCols?: number; } +interface MetricDatasourceState { + [prop: string]: unknown; + layers: IndexPatternLayer[]; +} + +export interface MetricSuggestion extends Suggestion { + datasourceState: MetricDatasourceState; + visualizationState: MetricVisualizationState; +} + export const supportedDataTypes = new Set(['number']); // TODO - deduplicate with gauges? @@ -484,4 +501,25 @@ export const getMetricVisualization = ({ noPadding: true, }; }, + + getSuggestionFromConvertToLensContext({ suggestions, context }) { + const allSuggestions = suggestions as MetricSuggestion[]; + return { + ...allSuggestions[0], + datasourceState: { + ...allSuggestions[0].datasourceState, + layers: allSuggestions.reduce( + (acc, s) => ({ + ...acc, + ...s.datasourceState.layers, + }), + {} + ), + }, + visualizationState: { + ...allSuggestions[0].visualizationState, + ...context.configuration, + }, + }; + }, }); diff --git a/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts b/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts index a7acd8bf5ba1..173ab1c4fdf0 100644 --- a/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts +++ b/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts @@ -102,9 +102,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await visualBuilder.clickDataTab('metric'); }); - it('should hide the "Edit Visualization in Lens" menu item', async () => { + it('should show the "Edit Visualization in Lens" menu item', async () => { const button = await testSubjects.exists('visualizeEditInLensButton'); - expect(button).to.eql(false); + expect(button).to.eql(true); + }); + + it('should convert to Lens', async () => { + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('mtrVis'); + + const metricData = await lens.getMetricVisualizationData(); + expect(metricData[0].title).to.eql('Count of records'); }); }); From 6993716021ae9683994feefcaaf949554beb3649 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Wed, 28 Sep 2022 12:08:58 +0200 Subject: [PATCH 054/185] skip failed tests (#142040) --- .../cypress/e2e/detection_rules/related_integrations.cy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts index b5c6b5cd341e..02ccff0c265d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts @@ -122,14 +122,14 @@ describe('Related integrations', () => { visit(DETECTIONS_RULE_MANAGEMENT_URL); }); - it('should display a badge with the installed integrations on the rule management page', () => { + it.skip('should display a badge with the installed integrations on the rule management page', () => { cy.get(INTEGRATIONS_POPOVER).should( 'have.text', `${rule.enabledIntegrations}/${rule.integrations.length} integrations` ); }); - it('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { + it.skip('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { openIntegrationsPopover(); cy.get(INTEGRATIONS_POPOVER_TITLE).should( @@ -148,7 +148,7 @@ describe('Related integrations', () => { }); }); - it('should display the integrations on the definition section', () => { + it.skip('should display the integrations on the definition section', () => { goToTheRuleDetailsOf(rule.name); cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); From 042c76687c46d16a227def1c04199af391bf4e31 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 28 Sep 2022 12:34:30 +0200 Subject: [PATCH 055/185] [ML] Update the Notification indicator tooltip, add functional tests (#141775) --- .../ml_page/notifications_indicator.tsx | 46 +++++-- .../components/notifications_list.tsx | 6 +- .../routes/schemas/notifications_schema.ts | 16 --- x-pack/test/api_integration/apis/ml/index.ts | 1 + .../ml/notifications/count_notifications.ts | 55 ++++++++ .../ml/notifications/get_notifications.ts | 102 +++++++++++++++ .../apis/ml/notifications/index.ts | 15 +++ .../functional/apps/ml/short_tests/index.ts | 1 + .../ml/short_tests/notifications/index.ts | 16 +++ .../notifications/notification_list.ts | 96 ++++++++++++++ x-pack/test/functional/services/ml/api.ts | 61 +++++++++ .../services/ml/common_table_service.ts | 118 ++++++++++++++++++ x-pack/test/functional/services/ml/index.ts | 8 +- .../functional/services/ml/notifications.ts | 48 +++++++ 14 files changed, 557 insertions(+), 32 deletions(-) create mode 100644 x-pack/test/api_integration/apis/ml/notifications/count_notifications.ts create mode 100644 x-pack/test/api_integration/apis/ml/notifications/get_notifications.ts create mode 100644 x-pack/test/api_integration/apis/ml/notifications/index.ts create mode 100644 x-pack/test/functional/apps/ml/short_tests/notifications/index.ts create mode 100644 x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts create mode 100644 x-pack/test/functional/services/ml/common_table_service.ts create mode 100644 x-pack/test/functional/services/ml/notifications.ts diff --git a/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx b/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx index 20d771f5654d..d0e3516af3db 100644 --- a/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx @@ -16,7 +16,10 @@ import { EuiToolTip, } from '@elastic/eui'; import { combineLatest, of, timer } from 'rxjs'; -import { catchError, filter, switchMap } from 'rxjs/operators'; +import { catchError, switchMap } from 'rxjs/operators'; +import moment from 'moment'; +import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; import { useAsObservable } from '../../hooks'; import { NotificationsCountResponse } from '../../../../common/types/notifications'; import { useMlKibana } from '../../contexts/kibana'; @@ -31,21 +34,26 @@ export const NotificationsIndicator: FC = () => { mlServices: { mlApiServices }, }, } = useMlKibana(); - const [lastCheckedAt] = useStorage(ML_NOTIFICATIONS_LAST_CHECKED_AT); + const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); + const [lastCheckedAt] = useStorage(ML_NOTIFICATIONS_LAST_CHECKED_AT); const lastCheckedAt$ = useAsObservable(lastCheckedAt); + /** Holds the value used for the actual request */ + const [lastCheckRequested, setLastCheckRequested] = useState(); const [notificationsCounts, setNotificationsCounts] = useState(); useEffect(function startPollingNotifications() { - const subscription = combineLatest([ - lastCheckedAt$.pipe(filter((v): v is number => !!v)), - timer(0, NOTIFICATIONS_CHECK_INTERVAL), - ]) + const subscription = combineLatest([lastCheckedAt$, timer(0, NOTIFICATIONS_CHECK_INTERVAL)]) .pipe( - switchMap(([lastChecked]) => - mlApiServices.notifications.countMessages$({ lastCheckedAt: lastChecked }) - ), + switchMap(([lastChecked]) => { + const lastCheckedAtQuery = lastChecked ?? moment().subtract(7, 'd').valueOf(); + setLastCheckRequested(lastCheckedAtQuery); + // Use the latest check time or 7 days ago by default. + return mlApiServices.notifications.countMessages$({ + lastCheckedAt: lastCheckedAtQuery, + }); + }), catchError((error) => { // Fail silently for now return of({} as NotificationsCountResponse); @@ -80,12 +88,22 @@ export const NotificationsIndicator: FC = () => { content={ } > - {errorsAndWarningCount} + + {errorsAndWarningCount} + ) : null} @@ -96,7 +114,8 @@ export const NotificationsIndicator: FC = () => { content={ } > @@ -106,6 +125,7 @@ export const NotificationsIndicator: FC = () => { aria-label={i18n.translate('xpack.ml.notificationsIndicator.unreadIcon', { defaultMessage: 'Unread notifications indicator.', })} + data-test-subj={'mlNotificationsIndicator'} /> diff --git a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx index 811b1c43bce3..9ea6aa1b70f0 100644 --- a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx +++ b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx @@ -162,6 +162,7 @@ export const NotificationsList: FC = () => { const columns: Array> = [ { + id: 'timestamp', field: 'timestamp', name: , sortable: true, @@ -175,7 +176,7 @@ export const NotificationsList: FC = () => { name: , sortable: true, truncateText: false, - 'data-test-subj': 'mlNotificationLabel', + 'data-test-subj': 'mlNotificationLevel', render: (value: MlNotificationMessageLevel) => { return {value}; }, @@ -194,7 +195,7 @@ export const NotificationsList: FC = () => { }, { field: 'job_id', - name: , + name: , sortable: true, truncateText: false, 'data-test-subj': 'mlNotificationEntity', @@ -320,6 +321,7 @@ export const NotificationsList: FC = () => { }, }, }, + 'data-test-subj': 'mlNotificationsSearchBarInput', }} filters={filters} onChange={(e) => { diff --git a/x-pack/plugins/ml/server/routes/schemas/notifications_schema.ts b/x-pack/plugins/ml/server/routes/schemas/notifications_schema.ts index a82691ee5281..153ffd0aeea8 100644 --- a/x-pack/plugins/ml/server/routes/schemas/notifications_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/notifications_schema.ts @@ -8,26 +8,10 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const getNotificationsQuerySchema = schema.object({ - /** - * Message level, e.g. info, error - */ - level: schema.maybe(schema.string()), - /** - * Message type, e.g. anomaly_detector - */ - type: schema.maybe(schema.string()), /** * Search string for the message content */ queryString: schema.maybe(schema.string()), - /** - * Page numer, zero-indexed - */ - from: schema.number({ defaultValue: 0 }), - /** - * Number of messages to return - */ - size: schema.number({ defaultValue: 10 }), /** * Sort field */ diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts index 915d755ca97c..e76eef8cb82b 100644 --- a/x-pack/test/api_integration/apis/ml/index.ts +++ b/x-pack/test/api_integration/apis/ml/index.ts @@ -67,5 +67,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./saved_objects')); loadTestFile(require.resolve('./system')); loadTestFile(require.resolve('./trained_models')); + loadTestFile(require.resolve('./notifications')); }); } diff --git a/x-pack/test/api_integration/apis/ml/notifications/count_notifications.ts b/x-pack/test/api_integration/apis/ml/notifications/count_notifications.ts new file mode 100644 index 000000000000..58932ea199b5 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/notifications/count_notifications.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import moment from 'moment'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + describe('GET notifications count', () => { + before(async () => { + await ml.api.initSavedObjects(); + await ml.testResources.setKibanaTimeZoneToUTC(); + + const adJobConfig = ml.commonConfig.getADFqSingleMetricJobConfig('fq_job'); + await ml.api.createAnomalyDetectionJob(adJobConfig); + + await ml.api.waitForJobNotificationsToIndex('fq_job'); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.cleanMLSavedObjects(); + }); + + it('return notifications count by level', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications/count`) + .query({ lastCheckedAt: moment().subtract(7, 'd').valueOf() }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(200, status, body); + + expect(body.info).to.eql(1); + expect(body.warning).to.eql(0); + expect(body.error).to.eql(0); + }); + + it('returns an error for unauthorized user', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications/count`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(403, status, body); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/notifications/get_notifications.ts b/x-pack/test/api_integration/apis/ml/notifications/get_notifications.ts new file mode 100644 index 000000000000..992065cdae67 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/notifications/get_notifications.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { + NotificationItem, + NotificationsSearchResponse, +} from '@kbn/ml-plugin/common/types/notifications'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('GET notifications', () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); + await ml.api.initSavedObjects(); + await ml.testResources.setKibanaTimeZoneToUTC(); + + const adJobConfig = ml.commonConfig.getADFqSingleMetricJobConfig('fq_job'); + await ml.api.createAnomalyDetectionJob(adJobConfig); + + const dfaJobConfig = ml.commonConfig.getDFABmClassificationJobConfig('df_job'); + await ml.api.createDataFrameAnalyticsJob(dfaJobConfig); + + // wait for notification to index + + await ml.api.waitForJobNotificationsToIndex('fq_job'); + await ml.api.waitForJobNotificationsToIndex('df_job'); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.cleanMLSavedObjects(); + }); + + it('return all notifications ', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications`) + .query({ earliest: 'now-1d', latest: 'now' }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(200, status, body); + + expect((body as NotificationsSearchResponse).total).to.eql(2); + }); + + it('return notifications based on the query string', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications`) + .query({ earliest: 'now-1d', latest: 'now', queryString: 'job_type:anomaly_detector' }) + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(200, status, body); + + expect((body as NotificationsSearchResponse).total).to.eql(1); + expect( + (body as NotificationsSearchResponse).results.filter( + (result: NotificationItem) => result.job_type === 'anomaly_detector' + ) + ).to.length(body.total); + }); + + it('supports sorting asc sorting by field', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications`) + .query({ earliest: 'now-1d', latest: 'now', sortField: 'job_id', sortDirection: 'asc' }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(200, status, body); + + expect(body.results[0].job_id).to.eql('df_job'); + }); + + it('supports sorting desc sorting by field', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications`) + .query({ earliest: 'now-1h', latest: 'now', sortField: 'job_id', sortDirection: 'desc' }) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(200, status, body); + + expect(body.results[0].job_id).to.eql('fq_job'); + }); + + it('returns an error for unauthorized user', async () => { + const { body, status } = await supertest + .get(`/api/ml/notifications`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_REQUEST_HEADERS); + ml.api.assertResponseStatusCode(403, status, body); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/notifications/index.ts b/x-pack/test/api_integration/apis/ml/notifications/index.ts new file mode 100644 index 000000000000..4a09fce5ee51 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/notifications/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Notifications', function () { + loadTestFile(require.resolve('./get_notifications')); + loadTestFile(require.resolve('./count_notifications')); + }); +} diff --git a/x-pack/test/functional/apps/ml/short_tests/index.ts b/x-pack/test/functional/apps/ml/short_tests/index.ts index f96d2b91ee0e..d446a3593347 100644 --- a/x-pack/test/functional/apps/ml/short_tests/index.ts +++ b/x-pack/test/functional/apps/ml/short_tests/index.ts @@ -33,5 +33,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./model_management')); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./settings')); + loadTestFile(require.resolve('./notifications')); }); } diff --git a/x-pack/test/functional/apps/ml/short_tests/notifications/index.ts b/x-pack/test/functional/apps/ml/short_tests/notifications/index.ts new file mode 100644 index 000000000000..e026d44a67af --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/notifications/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Notifcations', function () { + this.tags(['ml', 'skipFirefox']); + + loadTestFile(require.resolve('./notification_list')); + }); +} diff --git a/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts b/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts new file mode 100644 index 000000000000..c9ad8d2423ef --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common']); + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const browser = getService('browser'); + + describe('Notifications list', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + // Prepare jobs to generate notifications + await Promise.all( + [ + { jobId: 'fq_001', spaceId: undefined }, + { jobId: 'fq_002', spaceId: 'space1' }, + ].map(async (v) => { + const datafeedConfig = ml.commonConfig.getADFqDatafeedConfig(v.jobId); + + await ml.api.createAnomalyDetectionJob( + ml.commonConfig.getADFqSingleMetricJobConfig(v.jobId), + v.spaceId + ); + await ml.api.openAnomalyDetectionJob(v.jobId); + await ml.api.createDatafeed(datafeedConfig, v.spaceId); + await ml.api.startDatafeed(datafeedConfig.datafeed_id); + }) + ); + + await ml.securityUI.loginAsMlPowerUser(); + await PageObjects.common.navigateToApp('ml', { + basePath: '', + }); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.cleanMLSavedObjects(); + await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + }); + + it('displays a generic notification indicator', async () => { + await ml.notifications.assertNotificationIndicatorExist(); + }); + + it('opens the Notifications page', async () => { + await ml.navigation.navigateToNotifications(); + + await ml.notifications.table.waitForTableToLoad(); + await ml.notifications.table.assertRowsNumberPerPage(25); + }); + + it('does not show notifications from another space', async () => { + await ml.notifications.table.filterWithSearchString('Job created', 1); + }); + + it('display a number of errors in the notification indicator', async () => { + await ml.navigation.navigateToOverview(); + + const jobConfig = ml.commonConfig.getADFqSingleMetricJobConfig('fq_fail'); + jobConfig.analysis_config = { + bucket_span: '15m', + influencers: ['airline'], + detectors: [ + { function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'min', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'max', field_name: 'responsetime', partition_field_name: 'airline' }, + ], + }; + // Set extremely low memory limit to trigger an error + jobConfig.analysis_limits!.model_memory_limit = '1024kb'; + + const datafeedConfig = ml.commonConfig.getADFqDatafeedConfig(jobConfig.job_id); + + await ml.api.createAnomalyDetectionJob(jobConfig); + await ml.api.openAnomalyDetectionJob(jobConfig.job_id); + await ml.api.createDatafeed(datafeedConfig); + await ml.api.startDatafeed(datafeedConfig.datafeed_id); + await ml.api.waitForJobMemoryStatus(jobConfig.job_id, 'hard_limit'); + + // refresh the page to avoid 1m wait + await browser.refresh(); + await ml.notifications.assertNotificationErrorsCount(0); + }); + }); +} diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 07bd1f346cf2..62d46e644f17 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -273,6 +273,16 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { return state; }, + async getJobMemoryStatus(jobId: string): Promise<'hard_limit' | 'soft_limit' | 'ok'> { + const jobStats = await this.getADJobStats(jobId); + + expect(jobStats.jobs).to.have.length( + 1, + `Expected job stats to have exactly one job (got '${jobStats.length}')` + ); + return jobStats.jobs[0].model_size_stats.memory_status; + }, + async getADJobStats(jobId: string): Promise { log.debug(`Fetching anomaly detection job stats for job ${jobId}...`); const { body: jobStats, status } = await esSupertest.get( @@ -299,6 +309,27 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { }); }, + async waitForJobMemoryStatus( + jobId: string, + expectedMemoryStatus: 'hard_limit' | 'soft_limit' | 'ok', + timeout: number = 2 * 60 * 1000 + ) { + await retry.waitForWithTimeout( + `job memory status to be ${expectedMemoryStatus}`, + timeout, + async () => { + const memoryStatus = await this.getJobMemoryStatus(jobId); + if (memoryStatus === expectedMemoryStatus) { + return true; + } else { + throw new Error( + `expected job memory status to be ${expectedMemoryStatus} but got ${memoryStatus}` + ); + } + } + ); + }, + async getDatafeedState(datafeedId: string): Promise { log.debug(`Fetching datafeed state for datafeed ${datafeedId}`); const { body: datafeedStats, status } = await esSupertest.get( @@ -576,6 +607,18 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { return response; }, + async hasNotifications(query: object) { + const body = await es.search({ + index: '.ml-notifications*', + body: { + size: 10000, + query, + }, + }); + + return body.hits.hits.length > 0; + }, + async adJobExist(jobId: string) { this.validateJobId(jobId); try { @@ -608,6 +651,24 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { }); }, + async waitForJobNotificationsToIndex(jobId: string, timeout: number = 60 * 1000) { + await retry.waitForWithTimeout(`Notifications for '${jobId}' to exist`, timeout, async () => { + if ( + await this.hasNotifications({ + term: { + job_id: { + value: jobId, + }, + }, + }) + ) { + return true; + } else { + throw new Error(`expected '${jobId}' notifications to exist`); + } + }); + }, + async createAnomalyDetectionJob(jobConfig: Job, space?: string) { const jobId = jobConfig.job_id; log.debug( diff --git a/x-pack/test/functional/services/ml/common_table_service.ts b/x-pack/test/functional/services/ml/common_table_service.ts new file mode 100644 index 000000000000..50a40ab43f35 --- /dev/null +++ b/x-pack/test/functional/services/ml/common_table_service.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export type MlTableService = ReturnType; + +export function MlTableServiceProvider({ getPageObject, getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const commonPage = getPageObject('common'); + + const TableService = class { + constructor( + public readonly tableTestSubj: string, + public readonly tableRowSubj: string, + public readonly columns: Array<{ id: string; testSubj: string }>, + public readonly searchInputSubj: string + ) {} + + public async assertTableLoaded() { + await testSubjects.existOrFail(`~${this.tableTestSubj} loaded`); + } + + public async assertTableLoading() { + await testSubjects.existOrFail(`~${this.tableTestSubj} loading`); + } + + public async parseTable() { + const table = await testSubjects.find(`~${this.tableTestSubj}`); + const $ = await table.parseDomContent(); + const rows = []; + + for (const tr of $.findTestSubjects(`~${this.tableRowSubj}`).toArray()) { + const $tr = $(tr); + + const rowObject = this.columns.reduce((acc, curr) => { + acc[curr.id] = $tr + .findTestSubject(curr.testSubj) + .find('.euiTableCellContent') + .text() + .trim(); + return acc; + }, {} as Record); + + rows.push(rowObject); + } + + return rows; + } + + public async assertRowsNumberPerPage(rowsNumber: 10 | 25 | 50 | 100) { + const textContent = await testSubjects.getVisibleText( + `~${this.tableTestSubj} > tablePaginationPopoverButton` + ); + expect(textContent).to.be(`Rows per page: ${rowsNumber}`); + } + + public async waitForTableToStartLoading() { + await testSubjects.existOrFail(`~${this.tableTestSubj}`, { timeout: 60 * 1000 }); + await testSubjects.existOrFail(`${this.tableTestSubj} loading`, { timeout: 30 * 1000 }); + } + + public async waitForTableToLoad() { + await testSubjects.existOrFail(`~${this.tableTestSubj}`, { timeout: 60 * 1000 }); + await testSubjects.existOrFail(`${this.tableTestSubj} loaded`, { timeout: 30 * 1000 }); + } + + async getSearchInput(): Promise { + return await testSubjects.find(this.searchInputSubj); + } + + public async assertSearchInputValue(expectedSearchValue: string) { + const searchBarInput = await this.getSearchInput(); + const actualSearchValue = await searchBarInput.getAttribute('value'); + expect(actualSearchValue).to.eql( + expectedSearchValue, + `Search input value should be '${expectedSearchValue}' (got '${actualSearchValue}')` + ); + } + + public async filterWithSearchString(queryString: string, expectedRowCount: number = 1) { + await this.waitForTableToLoad(); + const searchBarInput = await this.getSearchInput(); + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(queryString); + await commonPage.pressEnterKey(); + await this.assertSearchInputValue(queryString); + await this.waitForTableToStartLoading(); + await this.waitForTableToLoad(); + + const rows = await this.parseTable(); + + expect(rows).to.have.length( + expectedRowCount, + `Filtered table should have ${expectedRowCount} row(s) for filter '${queryString}' (got ${rows.length} matching items)` + ); + } + }; + + return { + getServiceInstance( + name: string, + tableTestSubj: string, + tableRowSubj: string, + columns: Array<{ id: string; testSubj: string }>, + searchInputSubj: string + ) { + Object.defineProperty(TableService, 'name', { value: name }); + return new TableService(tableTestSubj, tableRowSubj, columns, searchInputSubj); + }, + }; +} diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index d8c6924ec4cf..9452baa32489 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -59,6 +59,8 @@ import { MachineLearningJobAnnotationsProvider } from './job_annotations_table'; import { MlNodesPanelProvider } from './ml_nodes_list'; import { MachineLearningCasesProvider } from './cases'; import { AnomalyChartsProvider } from './anomaly_charts'; +import { NotificationsProvider } from './notifications'; +import { MlTableServiceProvider } from './common_table_service'; export function MachineLearningProvider(context: FtrProviderContext) { const commonAPI = MachineLearningCommonAPIProvider(context); @@ -123,6 +125,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const settingsFilterList = MachineLearningSettingsFilterListProvider(context, commonUI); const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context, commonUI); const stackManagementJobs = MachineLearningStackManagementJobsProvider(context); + const tableService = MlTableServiceProvider(context); const testExecution = MachineLearningTestExecutionProvider(context); const testResources = MachineLearningTestResourcesProvider(context, api); const alerting = MachineLearningAlertingProvider(context, api, commonUI); @@ -130,6 +133,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const trainedModels = TrainedModelsProvider(context, commonUI); const trainedModelsTable = TrainedModelsTableProvider(context, commonUI); const mlNodesPanel = MlNodesPanelProvider(context); + const notifications = NotificationsProvider(context, commonUI, tableService); const cases = MachineLearningCasesProvider(context, swimLane, anomalyCharts); @@ -171,7 +175,9 @@ export function MachineLearningProvider(context: FtrProviderContext) { jobWizardMultiMetric, jobWizardPopulation, lensVisualizations, + mlNodesPanel, navigation, + notifications, overviewPage, securityCommon, securityUI, @@ -181,10 +187,10 @@ export function MachineLearningProvider(context: FtrProviderContext) { singleMetricViewer, stackManagementJobs, swimLane, + tableService, testExecution, testResources, trainedModels, trainedModelsTable, - mlNodesPanel, }; } diff --git a/x-pack/test/functional/services/ml/notifications.ts b/x-pack/test/functional/services/ml/notifications.ts new file mode 100644 index 000000000000..2365717d7226 --- /dev/null +++ b/x-pack/test/functional/services/ml/notifications.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { MlCommonUI } from './common_ui'; +import { MlTableService } from './common_table_service'; + +export function NotificationsProvider( + { getService }: FtrProviderContext, + mlCommonUI: MlCommonUI, + tableService: MlTableService +) { + const testSubjects = getService('testSubjects'); + + return { + async assertNotificationIndicatorExist(expectExist = true) { + if (expectExist) { + await testSubjects.existOrFail('mlNotificationsIndicator'); + } else { + await testSubjects.missingOrFail('mlNotificationsIndicator'); + } + }, + + async assertNotificationErrorsCount(expectedCount: number) { + const actualCount = await testSubjects.getVisibleText('mlNotificationErrorsIndicator'); + expect(actualCount).to.greaterThan(expectedCount); + }, + + table: tableService.getServiceInstance( + 'NotificationsTable', + 'mlNotificationsTable', + 'mlNotificationsTableRow', + [ + { id: 'timestamp', testSubj: 'mlNotificationTime' }, + { id: 'level', testSubj: 'mlNotificationLevel' }, + { id: 'job_type', testSubj: 'mlNotificationType' }, + { id: 'job_id', testSubj: 'mlNotificationEntity' }, + { id: 'message', testSubj: 'mlNotificationMessage' }, + ], + 'mlNotificationsSearchBarInput' + ), + }; +} From 0471095d7d01634ffae4ef5017797e28ff5b13a8 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 28 Sep 2022 13:02:51 +0200 Subject: [PATCH 056/185] Run ILM jest tests as integration tests allowing them to run beyond 5s timeout (#141750) --- .../index_lifecycle_management/README.md | 6 +++++- .../__jest__/extend_index_management.test.tsx | 2 +- .../integration_tests/README.md | 14 ++++++++++++++ .../app/app.helpers.ts | 2 +- .../app/app.test.ts | 0 .../edit_policy/constants.ts | 4 ++-- .../features/delete_phase.helpers.ts | 0 .../edit_policy/features/delete_phase.test.ts | 2 +- .../features/downsample.helpers.ts | 2 +- .../edit_policy/features/downsample.test.ts | 0 .../edit_policy/features/edit_warning.test.ts | 0 .../edit_policy/features/frozen_phase.test.ts | 0 .../cloud_aware_behavior.helpers.ts | 2 +- .../cloud_aware_behavior.test.ts | 0 .../node_allocation/cold_phase.helpers.ts | 0 .../node_allocation/cold_phase.test.ts | 0 .../general_behavior.helpers.ts | 0 .../node_allocation/general_behavior.test.ts | 2 +- .../node_allocation/warm_phase.helpers.ts | 0 .../node_allocation/warm_phase.test.ts | 0 .../features/request_flyout.helpers.ts | 0 .../features/request_flyout.test.ts | 0 .../edit_policy/features/rollover.helpers.ts | 0 .../edit_policy/features/rollover.test.ts | 0 .../features/searchable_snapshots.helpers.ts | 2 +- .../features/searchable_snapshots.test.ts | 2 +- .../edit_policy/features/timeline.helpers.ts | 2 +- .../edit_policy/features/timeline.test.ts | 0 .../edit_policy/features/timing.helpers.ts | 0 .../edit_policy/features/timing.test.ts | 2 +- .../cold_phase_validation.test.ts | 5 ++--- .../downsample_interval.test.ts | 7 +++---- .../form_validation/error_indicators.test.ts | 3 +-- .../hot_phase_validation.test.ts | 5 ++--- .../policy_name_validation.test.ts | 5 ++--- .../form_validation/timing.test.ts | 7 +++---- .../form_validation/validation.helpers.ts | 0 .../warm_phase_validation.test.ts | 5 ++--- .../edit_policy/init_test_bed.ts | 6 +++--- .../policy_serialization.helpers.ts | 2 +- .../policy_serialization.test.ts | 2 +- .../helpers/actions/downsample_actions.ts | 4 ++-- .../helpers/actions/errors_actions.ts | 2 +- .../helpers/actions/forcemerge_actions.ts | 2 +- .../helpers/actions/form_set_value_action.ts | 0 .../helpers/actions/form_toggle_action.ts | 0 .../form_toggle_and_set_value_action.ts | 0 .../helpers/actions/index.ts | 0 .../helpers/actions/index_priority_actions.ts | 2 +- .../helpers/actions/min_age_actions.ts | 2 +- .../actions/node_allocation_actions.ts | 4 ++-- .../helpers/actions/phases.ts | 0 .../helpers/actions/readonly_actions.ts | 2 +- .../helpers/actions/replicas_action.ts | 2 +- .../helpers/actions/request_flyout_actions.ts | 0 .../helpers/actions/rollover_actions.ts | 0 .../helpers/actions/save_policy_action.ts | 0 .../actions/searchable_snapshot_actions.ts | 2 +- .../helpers/actions/shrink_actions.ts | 2 +- .../actions/snapshot_policy_actions.ts | 0 .../helpers/actions/toggle_phase_action.ts | 2 +- .../helpers/global_mocks.tsx | 0 .../helpers/http_requests.ts | 4 ++-- .../helpers/index.ts | 0 .../helpers/setup_environment.tsx | 10 +++++----- .../jest.integration.config.js | 19 +++++++++++++++++++ .../index_lifecycle_management/tsconfig.json | 1 + 67 files changed, 91 insertions(+), 60 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/integration_tests/README.md rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/app/app.helpers.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/app/app.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/constants.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/delete_phase.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/delete_phase.test.ts (98%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/downsample.helpers.ts (95%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/downsample.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/edit_warning.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/frozen_phase.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts (94%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/cold_phase.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/cold_phase.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/general_behavior.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/general_behavior.test.ts (98%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/warm_phase.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/node_allocation/warm_phase.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/request_flyout.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/request_flyout.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/rollover.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/rollover.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/searchable_snapshots.helpers.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/searchable_snapshots.test.ts (99%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/timeline.helpers.ts (94%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/timeline.test.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/timing.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/features/timing.test.ts (95%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/cold_phase_validation.test.ts (91%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/downsample_interval.test.ts (93%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/error_indicators.test.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/hot_phase_validation.test.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/policy_name_validation.test.ts (93%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/timing.test.ts (93%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/validation.helpers.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/form_validation/warm_phase_validation.test.ts (95%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/init_test_bed.ts (89%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/serialization/policy_serialization.helpers.ts (95%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/edit_policy/serialization/policy_serialization.test.ts (99%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/downsample_actions.ts (96%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/errors_actions.ts (96%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/forcemerge_actions.ts (96%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/form_set_value_action.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/form_toggle_action.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/form_toggle_and_set_value_action.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/index.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/index_priority_actions.ts (94%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/min_age_actions.ts (94%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/node_allocation_actions.ts (95%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/phases.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/readonly_actions.ts (92%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/replicas_action.ts (92%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/request_flyout_actions.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/rollover_actions.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/save_policy_action.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/searchable_snapshot_actions.ts (96%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/shrink_actions.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/snapshot_policy_actions.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/actions/toggle_phase_action.ts (96%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/global_mocks.tsx (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/http_requests.ts (97%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/index.ts (100%) rename x-pack/plugins/index_lifecycle_management/{__jest__/client_integration => integration_tests}/helpers/setup_environment.tsx (80%) create mode 100644 x-pack/plugins/index_lifecycle_management/jest.integration.config.js diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md index 35c2aa063ec2..912b19295790 100644 --- a/x-pack/plugins/index_lifecycle_management/README.md +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -115,4 +115,8 @@ this by running: ```bash yarn es snapshot --license=trial -``` \ No newline at end of file +``` + +## Integration tests + +See `./integration_tests/README.md` \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx index ecefe8132c49..246de6e8ed25 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/extend_index_management.test.tsx @@ -7,7 +7,7 @@ import moment from 'moment-timezone'; -import { init } from './client_integration/helpers/http_requests'; +import { init } from '../integration_tests/helpers/http_requests'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; import { Index } from '../common/types'; diff --git a/x-pack/plugins/index_lifecycle_management/integration_tests/README.md b/x-pack/plugins/index_lifecycle_management/integration_tests/README.md new file mode 100644 index 000000000000..5e4bf4360cb7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/README.md @@ -0,0 +1,14 @@ +Most plugins of the deployment management team follow +the similar testing infrastructure where integration tests are located in `__jest__` and run as unit tests. + +The `index_lifecycle_management` tests [were refactored](https://github.com/elastic/kibana/pull/141750) to be run as integration tests because they [became flaky hitting the 5 seconds timeout](https://github.com/elastic/kibana/issues/115307#issuecomment-1238417474) for a jest unit test. + +Jest integration tests are just sit in a different directory and have two main differences: +- They never use parallelism, this allows them to access file system resources, launch services, etc. without needing to worry about conflicts with other tests +- They are allowed to take their sweet time, the default timeout is currently 10 minutes. + +To run these tests use: + +``` +node scripts/jest_integration x-pack/plugins/index_lifecycle_management/ +``` \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/app/app.helpers.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/app/app.helpers.ts index df64fbce3fa1..66810ebb1e54 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/app/app.helpers.ts @@ -8,7 +8,7 @@ import { act } from 'react-dom/test-utils'; import { HttpSetup } from '@kbn/core/public'; import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test-jest-helpers'; -import { App } from '../../../public/application/app'; +import { App } from '../../public/application/app'; import { WithAppDependencies } from '../helpers'; const getTestBedConfig = (initialEntries: string[]): TestBedConfig => ({ diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/app/app.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/app/app.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/constants.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/constants.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts index 620cb9d6f8dd..a41bea1cb841 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/constants.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts @@ -7,9 +7,9 @@ import moment from 'moment-timezone'; -import { PolicyFromES } from '../../../common/types'; +import { PolicyFromES } from '../../common/types'; -import { defaultRolloverAction } from '../../../public/application/constants'; +import { defaultRolloverAction } from '../../public/application/constants'; export const POLICY_NAME = 'my_policy'; export const SNAPSHOT_POLICY_NAME = 'my_snapshot_policy'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/delete_phase.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/delete_phase.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/delete_phase.test.ts similarity index 98% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/delete_phase.test.ts index d877f05d06ae..df6dd516c8a5 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/delete_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/delete_phase.test.ts @@ -6,7 +6,7 @@ */ import { act } from 'react-dom/test-utils'; -import { API_BASE_PATH } from '../../../../common/constants'; +import { API_BASE_PATH } from '../../../common/constants'; import { setupEnvironment } from '../../helpers'; import { DELETE_PHASE_POLICY, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/downsample.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/downsample.helpers.ts similarity index 95% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/downsample.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/downsample.helpers.ts index be96aaabe4a7..1e6ed336a5f0 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/downsample.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/downsample.helpers.ts @@ -14,7 +14,7 @@ import { createTogglePhaseAction, } from '../../helpers'; import { initTestBed } from '../init_test_bed'; -import { AppServicesContext } from '../../../../public/types'; +import { AppServicesContext } from '../../../public/types'; type SetupReturn = ReturnType; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/downsample.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/downsample.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/downsample.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/downsample.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/edit_warning.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/edit_warning.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/edit_warning.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/frozen_phase.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/frozen_phase.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts similarity index 94% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts index 7092d52289de..b41075c6b6b6 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts @@ -8,7 +8,7 @@ import { HttpSetup } from '@kbn/core/public'; import { TestBedConfig } from '@kbn/test-jest-helpers'; -import { AppServicesContext } from '../../../../../public/types'; +import { AppServicesContext } from '../../../../public/types'; import { createTogglePhaseAction, createNodeAllocationActions } from '../../../helpers'; import { initTestBed } from '../../init_test_bed'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cold_phase.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cold_phase.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cold_phase.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/cold_phase.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/general_behavior.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/general_behavior.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/general_behavior.test.ts similarity index 98% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/general_behavior.test.ts index 1eecd5207664..4830cee8ee23 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/general_behavior.test.ts @@ -17,7 +17,7 @@ import { POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, POLICY_WITH_NODE_ROLE_ALLOCATION, } from '../../constants'; -import { API_BASE_PATH } from '../../../../../common/constants'; +import { API_BASE_PATH } from '../../../../common/constants'; describe(' node allocation general behavior', () => { let testBed: GeneralNodeAllocationTestBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/warm_phase.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/warm_phase.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/warm_phase.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/node_allocation/warm_phase.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/request_flyout.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/request_flyout.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/request_flyout.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/request_flyout.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/request_flyout.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/rollover.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/rollover.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/rollover.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/rollover.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.helpers.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.helpers.ts index e70f721a4807..d3b68ffc6a13 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.helpers.ts @@ -18,7 +18,7 @@ import { createTogglePhaseAction, } from '../../helpers'; import { initTestBed } from '../init_test_bed'; -import { AppServicesContext } from '../../../../public/types'; +import { AppServicesContext } from '../../../public/types'; type SetupReturn = ReturnType; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.test.ts similarity index 99% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.test.ts index 03b7670c1eac..68e74e23a781 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/searchable_snapshots.test.ts @@ -10,7 +10,7 @@ import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { HttpFetchOptionsWithPath } from '@kbn/core/public'; import { setupEnvironment } from '../../helpers'; import { getDefaultHotPhasePolicy } from '../constants'; -import { API_BASE_PATH } from '../../../../common/constants'; +import { API_BASE_PATH } from '../../../common/constants'; import { SearchableSnapshotsTestBed, setupSearchableSnapshotsTestBed, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timeline.helpers.ts similarity index 94% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timeline.helpers.ts index 496b27330c93..202388a84446 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timeline.helpers.ts @@ -8,7 +8,7 @@ import { HttpSetup } from '@kbn/core/public'; import { createTogglePhaseAction } from '../../helpers'; import { initTestBed } from '../init_test_bed'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; type SetupReturn = ReturnType; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timeline.test.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timeline.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timeline.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timing.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timing.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timing.test.ts similarity index 95% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timing.test.ts index 0aee8eb5f0be..72c6a2a4e789 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/features/timing.test.ts @@ -8,7 +8,7 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment } from '../../helpers'; import { setupTimingTestBed, TimingTestBed } from './timing.helpers'; -import { PhaseWithTiming } from '../../../../common/types'; +import { PhaseWithTiming } from '../../../common/types'; describe(' timing', () => { let testBed: TimingTestBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/cold_phase_validation.test.ts similarity index 91% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/cold_phase_validation.test.ts index ef2fc67002c1..e75d2cb72ab2 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/cold_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/cold_phase_validation.test.ts @@ -6,12 +6,11 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141645 -describe.skip(' cold phase validation', () => { +describe(' cold phase validation', () => { let testBed: ValidationTestBed; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/downsample_interval.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/downsample_interval.test.ts similarity index 93% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/downsample_interval.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/downsample_interval.test.ts index 79f5fdc6e284..d2f9943eba68 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/downsample_interval.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/downsample_interval.test.ts @@ -6,14 +6,13 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; -import { PhaseWithDownsample } from '../../../../common/types'; +import { PhaseWithDownsample } from '../../../common/types'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141160 -describe.skip(' downsample interval validation', () => { +describe(' downsample interval validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/error_indicators.test.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/error_indicators.test.ts index 349f98766620..bd4a2caec0be 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/error_indicators.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/error_indicators.test.ts @@ -9,8 +9,7 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141645 -describe.skip(' error indicators', () => { +describe(' error indicators', () => { let testBed: ValidationTestBed; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/hot_phase_validation.test.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/hot_phase_validation.test.ts index 82b3568d39ce..71f83a59360d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/hot_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/hot_phase_validation.test.ts @@ -6,12 +6,11 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141645 -describe.skip(' hot phase validation', () => { +describe(' hot phase validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/policy_name_validation.test.ts similarity index 93% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/policy_name_validation.test.ts index 928380e3d1ea..c530f73a66c1 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/policy_name_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/policy_name_validation.test.ts @@ -6,13 +6,12 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; import { setupEnvironment } from '../../helpers'; import { getGeneratedPolicies } from '../constants'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141645 -describe.skip(' policy name validation', () => { +describe(' policy name validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/timing.test.ts similarity index 93% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/timing.test.ts index 7db483d6d0ef..5838f04ba70e 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/timing.test.ts @@ -6,14 +6,13 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; -import { PhaseWithTiming } from '../../../../common/types'; +import { PhaseWithTiming } from '../../../common/types'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/115307 -describe.skip(' timing validation', () => { +describe(' timing validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/validation.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/validation.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/validation.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/warm_phase_validation.test.ts similarity index 95% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/warm_phase_validation.test.ts index df0607a0a0e6..47917b1f8e3d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/warm_phase_validation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/form_validation/warm_phase_validation.test.ts @@ -6,12 +6,11 @@ */ import { act } from 'react-dom/test-utils'; -import { i18nTexts } from '../../../../public/application/sections/edit_policy/i18n_texts'; +import { i18nTexts } from '../../../public/application/sections/edit_policy/i18n_texts'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/141645 -describe.skip(' warm phase validation', () => { +describe(' warm phase validation', () => { let testBed: ValidationTestBed; const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/init_test_bed.ts similarity index 89% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/init_test_bed.ts index 875bf9db3626..56bed7a6e50a 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/init_test_bed.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { HttpSetup } from '@kbn/core/public'; import { registerTestBed, TestBedConfig } from '@kbn/test-jest-helpers'; -import { HttpSetup } from '@kbn/core/public'; import { WithAppDependencies } from '../helpers'; -import { EditPolicy } from '../../../public/application/sections/edit_policy'; -import { AppServicesContext } from '../../../public/types'; +import { EditPolicy } from '../../public/application/sections/edit_policy'; +import { AppServicesContext } from '../../public/types'; import { POLICY_NAME } from './constants'; const getTestBedConfig = (testBedConfig?: Partial): TestBedConfig => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.helpers.ts similarity index 95% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.helpers.ts index 417f53c63a20..e439fca0de51 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.helpers.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.helpers.ts @@ -6,7 +6,7 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { AppServicesContext } from '../../../../public/types'; +import { AppServicesContext } from '../../../public/types'; import { createColdPhaseActions, createDeletePhaseActions, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.test.ts similarity index 99% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.test.ts index 983cfbadfa01..05aa66fcc5f2 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/serialization/policy_serialization.test.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/serialization/policy_serialization.test.ts @@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { HttpFetchOptionsWithPath } from '@kbn/core/public'; import { setupEnvironment } from '../../helpers'; -import { API_BASE_PATH } from '../../../../common/constants'; +import { API_BASE_PATH } from '../../../common/constants'; import { getDefaultHotPhasePolicy, POLICY_WITH_INCLUDE_EXCLUDE, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/downsample_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/downsample_actions.ts similarity index 96% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/downsample_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/downsample_actions.ts index 315ed3d58520..a389a9deebe3 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/downsample_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/downsample_actions.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { TestBed } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; -import { Phase } from '../../../../common/types'; +import { TestBed } from '@kbn/test-jest-helpers'; +import { Phase } from '../../../common/types'; import { createFormToggleAction } from '..'; const createSetDownsampleIntervalAction = diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/errors_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/errors_actions.ts similarity index 96% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/errors_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/errors_actions.ts index 4b863071e191..18f07734fade 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/errors_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/errors_actions.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; const createWaitForValidationAction = (testBed: TestBed) => () => { const { component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/forcemerge_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/forcemerge_actions.ts similarity index 96% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/forcemerge_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/forcemerge_actions.ts index b6ed40de36ca..619d6bd8be85 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/forcemerge_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/forcemerge_actions.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormToggleAction } from './form_toggle_action'; import { createFormSetValueAction } from './form_set_value_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_set_value_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_set_value_action.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_set_value_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_set_value_action.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_toggle_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_toggle_action.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_toggle_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_toggle_action.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_toggle_and_set_value_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_toggle_and_set_value_action.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/form_toggle_and_set_value_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/form_toggle_and_set_value_action.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/index.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/index.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/index.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/index.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/index_priority_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/index_priority_actions.ts similarity index 94% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/index_priority_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/index_priority_actions.ts index 79fb77e53cc5..4c6e4604be7d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/index_priority_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/index_priority_actions.ts @@ -6,7 +6,7 @@ */ import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormToggleAction } from './form_toggle_action'; import { createFormToggleAndSetValueAction } from './form_toggle_and_set_value_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/min_age_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/min_age_actions.ts similarity index 94% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/min_age_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/min_age_actions.ts index ef00fdc8d775..8b4fa2e5f617 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/min_age_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/min_age_actions.ts @@ -6,7 +6,7 @@ */ import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormSetValueAction } from './form_set_value_action'; export const createMinAgeActions = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/node_allocation_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/node_allocation_actions.ts similarity index 95% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/node_allocation_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/node_allocation_actions.ts index 2a680590654a..a3cb607b60f9 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/node_allocation_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/node_allocation_actions.ts @@ -8,8 +8,8 @@ import { act } from 'react-dom/test-utils'; import { TestBed } from '@kbn/test-jest-helpers'; -import { DataTierAllocationType } from '../../../../public/application/sections/edit_policy/types'; -import { Phase } from '../../../../common/types'; +import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; +import { Phase } from '../../../common/types'; import { createFormSetValueAction } from './form_set_value_action'; export const createNodeAllocationActions = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/phases.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/phases.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/phases.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/phases.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/readonly_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/readonly_actions.ts similarity index 92% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/readonly_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/readonly_actions.ts index 1a7ec56815b0..fe4c71d76265 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/readonly_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/readonly_actions.ts @@ -6,7 +6,7 @@ */ import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormToggleAction } from './form_toggle_action'; export const createReadonlyActions = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/replicas_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/replicas_action.ts similarity index 92% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/replicas_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/replicas_action.ts index 43eca26a4e1c..35c3afc60751 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/replicas_action.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/replicas_action.ts @@ -6,7 +6,7 @@ */ import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormToggleAndSetValueAction } from './form_toggle_and_set_value_action'; export const createReplicasAction = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/request_flyout_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/request_flyout_actions.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/request_flyout_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/request_flyout_actions.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/rollover_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/rollover_actions.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/rollover_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/rollover_actions.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/save_policy_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/save_policy_action.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/save_policy_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/save_policy_action.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/searchable_snapshot_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/searchable_snapshot_actions.ts similarity index 96% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/searchable_snapshot_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/searchable_snapshot_actions.ts index 3efffcddbece..c9a019c2bc84 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/searchable_snapshot_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/searchable_snapshot_actions.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; import { TestBed } from '@kbn/test-jest-helpers'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormToggleAction } from './form_toggle_action'; export const createSearchableSnapshotActions = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/shrink_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/shrink_actions.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/shrink_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/shrink_actions.ts index def20f73b82f..4bf39185b8c0 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/shrink_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/shrink_actions.ts @@ -7,7 +7,7 @@ import { TestBed } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; import { createFormSetValueAction } from './form_set_value_action'; export const createShrinkActions = (testBed: TestBed, phase: Phase) => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/snapshot_policy_actions.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/snapshot_policy_actions.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/snapshot_policy_actions.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/snapshot_policy_actions.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts similarity index 96% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts index c22efae87d5a..fc89332e47a6 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/actions/toggle_phase_action.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts @@ -8,7 +8,7 @@ import { TestBed } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; -import { Phase } from '../../../../common/types'; +import { Phase } from '../../../common/types'; const toggleDeletePhase = async (testBed: TestBed) => { const { find, component } = testBed; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/global_mocks.tsx similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/global_mocks.tsx diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/http_requests.ts similarity index 97% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/http_requests.ts index 7ddcd5358404..70e85c8bc5df 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/http_requests.ts @@ -6,12 +6,12 @@ */ import { httpServiceMock } from '@kbn/core/public/mocks'; -import { API_BASE_PATH } from '../../../common/constants'; +import { API_BASE_PATH } from '../../common/constants'; import { ListNodesRouteResponse, ListSnapshotReposResponse, NodesDetailsResponse, -} from '../../../common/types'; +} from '../../common/types'; import { getDefaultHotPhasePolicy } from '../edit_policy/constants'; type HttpMethod = 'GET' | 'PUT' | 'DELETE' | 'POST'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/index.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/index.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/setup_environment.tsx similarity index 80% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx rename to x-pack/plugins/index_lifecycle_management/integration_tests/helpers/setup_environment.tsx index 055097c883e9..91aebb485ea7 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/setup_environment.tsx @@ -19,12 +19,12 @@ import { executionContextServiceMock, } from '@kbn/core/public/mocks'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; -import { init as initHttp } from '../../../public/application/services/http'; +import { init as initHttp } from '../../public/application/services/http'; import { init as initHttpRequests } from './http_requests'; -import { init as initUiMetric } from '../../../public/application/services/ui_metric'; -import { init as initNotification } from '../../../public/application/services/notification'; -import { KibanaContextProvider } from '../../../public/shared_imports'; -import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; +import { init as initUiMetric } from '../../public/application/services/ui_metric'; +import { init as initNotification } from '../../public/application/services/notification'; +import { KibanaContextProvider } from '../../public/shared_imports'; +import { createBreadcrumbsMock } from '../../public/application/services/breadcrumbs.mock'; const breadcrumbService = createBreadcrumbsMock(); const appContextMock = { diff --git a/x-pack/plugins/index_lifecycle_management/jest.integration.config.js b/x-pack/plugins/index_lifecycle_management/jest.integration.config.js new file mode 100644 index 000000000000..6d1ac5ec2fd8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/jest.integration.config.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test/jest_integration', + rootDir: '../../..', + roots: ['/x-pack/plugins/index_lifecycle_management'], + testMatch: ['/**/integration_tests/**/*.test.{js,mjs,ts,tsx}'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/index_lifecycle_management', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/index_lifecycle_management/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/x-pack/plugins/index_lifecycle_management/tsconfig.json b/x-pack/plugins/index_lifecycle_management/tsconfig.json index d3a342e11021..4b5d7657ed9f 100644 --- a/x-pack/plugins/index_lifecycle_management/tsconfig.json +++ b/x-pack/plugins/index_lifecycle_management/tsconfig.json @@ -8,6 +8,7 @@ }, "include": [ "__jest__/**/*", + "integration_tests/**/*", "common/**/*", "public/**/*", "server/**/*", From 0fbbd4f18ae78e4346366152192c4b2e0455ba24 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Wed, 28 Sep 2022 07:35:51 -0400 Subject: [PATCH 057/185] [Alerting][Docs] Adding link to ES docs for CCS setup (#141995) * Adding link to ES docs * Adding link to ES docs * Apply suggestions from code review Co-authored-by: Lisa Cawley Co-authored-by: Lisa Cawley --- docs/user/alerting/alerting-setup.asciidoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/user/alerting/alerting-setup.asciidoc b/docs/user/alerting/alerting-setup.asciidoc index 819f20005d7a..36c8b46e7b80 100644 --- a/docs/user/alerting/alerting-setup.asciidoc +++ b/docs/user/alerting/alerting-setup.asciidoc @@ -98,3 +98,10 @@ user without those privileges updates the rule, the rule will no longer function. Conversely, if a user with greater or administrator privileges modifies the rule, it will begin running with increased privileges. ============================================== + +[float] +[[alerting-ccs-setup]] +=== {ccs-cap} + +If you want to use alerting rules with {ccs}, you must configure +{ref}/remote-clusters-privileges.html#clusters-privileges-ccs-kibana[privileges for {ccs-init} and {kib}]. \ No newline at end of file From 82492c791a3e1c27a88b8ceeecbe7a1d35c20ca5 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Wed, 28 Sep 2022 13:45:14 +0200 Subject: [PATCH 058/185] [Infra Monitoring UI] Change infrastructure pages titles to use breadcrumbs #135874 (#140824) * Use breadcrumbs to set page title intead of DocumentTitle * Remove document title component and use breadcrumbs - Logs Page * Remove no longer needed document title translations * Use docTitle.change in error page and remove document title component * Add document title check to the functional tests * Move test to check title after load * test: Title change * Remove unnecessary docTitle type * Add error page tests * Update snapshot * Functional tests: Wait for loading the header before checking the title * Change test to use react testing library Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/infra/public/apps/metrics_app.tsx | 1 + .../public/components/document_title.tsx | 72 ------------------- .../infra/public/hooks/use_breadcrumbs.ts | 9 ++- .../infra/public/hooks/use_document_title.tsx | 25 +++++++ .../infra/public/pages/logs/page_content.tsx | 3 - .../infra/public/pages/logs/stream/page.tsx | 2 - .../public/pages/logs/stream/page_header.tsx | 28 -------- .../public/pages/metrics/hosts/index.tsx | 13 ---- .../infra/public/pages/metrics/index.tsx | 7 -- .../pages/metrics/inventory_view/index.tsx | 14 ---- .../components/page_error.test.tsx | 47 ++++++++++++ .../metric_detail/components/page_error.tsx | 16 ++--- .../pages/metrics/metric_detail/index.tsx | 9 --- .../pages/metrics/metrics_explorer/index.tsx | 11 --- x-pack/plugins/infra/public/translations.ts | 4 ++ .../translations/translations/fr-FR.json | 7 +- .../translations/translations/ja-JP.json | 7 +- .../translations/translations/zh-CN.json | 7 +- .../test/functional/apps/infra/home_page.ts | 26 ++++++- x-pack/test/functional/apps/infra/link_to.ts | 2 + .../apps/infra/logs_source_configuration.ts | 10 +++ .../functional/apps/infra/metrics_explorer.ts | 8 +++ 22 files changed, 136 insertions(+), 192 deletions(-) delete mode 100644 x-pack/plugins/infra/public/components/document_title.tsx create mode 100644 x-pack/plugins/infra/public/hooks/use_document_title.tsx delete mode 100644 x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.test.tsx diff --git a/x-pack/plugins/infra/public/apps/metrics_app.tsx b/x-pack/plugins/infra/public/apps/metrics_app.tsx index fdeb7173d5ce..ce8123b5f222 100644 --- a/x-pack/plugins/infra/public/apps/metrics_app.tsx +++ b/x-pack/plugins/infra/public/apps/metrics_app.tsx @@ -45,6 +45,7 @@ export const renderApp = ( ); return () => { + core.chrome.docTitle.reset(); ReactDOM.unmountComponentAtNode(element); plugins.data.search.session.clear(); }; diff --git a/x-pack/plugins/infra/public/components/document_title.tsx b/x-pack/plugins/infra/public/components/document_title.tsx deleted file mode 100644 index 20e482d9df5b..000000000000 --- a/x-pack/plugins/infra/public/components/document_title.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -type TitleProp = string | ((previousTitle: string) => string); - -interface DocumentTitleProps { - title: TitleProp; -} - -interface DocumentTitleState { - index: number; -} - -const wrapWithSharedState = () => { - const titles: string[] = []; - const TITLE_SUFFIX = ' - Kibana'; - - return class extends React.Component { - public componentDidMount() { - this.setState( - () => { - return { index: titles.push('') - 1 }; - }, - () => { - this.pushTitle(this.getTitle(this.props.title)); - this.updateDocumentTitle(); - } - ); - } - - public componentDidUpdate() { - this.pushTitle(this.getTitle(this.props.title)); - this.updateDocumentTitle(); - } - - public componentWillUnmount() { - this.removeTitle(); - this.updateDocumentTitle(); - } - - public render() { - return null; - } - - public getTitle(title: TitleProp) { - return typeof title === 'function' ? title(titles[this.state.index - 1]) : title; - } - - public pushTitle(title: string) { - titles[this.state.index] = title; - } - - public removeTitle() { - titles.pop(); - } - - public updateDocumentTitle() { - const title = (titles[titles.length - 1] || '') + TITLE_SUFFIX; - if (title !== document.title) { - document.title = title; - } - } - }; -}; - -export const DocumentTitle = wrapWithSharedState(); diff --git a/x-pack/plugins/infra/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/infra/public/hooks/use_breadcrumbs.ts index 37801c16bcf1..97f737380022 100644 --- a/x-pack/plugins/infra/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/infra/public/hooks/use_breadcrumbs.ts @@ -22,7 +22,7 @@ export const useBreadcrumbs = (app: AppId, appTitle: string, extraCrumbs: Chrome const appLinkProps = useLinkProps({ app }); useEffect(() => { - chrome?.setBreadcrumbs?.([ + const breadcrumbs = [ { ...observabilityLinkProps, text: observabilityTitle, @@ -32,6 +32,11 @@ export const useBreadcrumbs = (app: AppId, appTitle: string, extraCrumbs: Chrome text: appTitle, }, ...extraCrumbs, - ]); + ]; + + const docTitle = [...breadcrumbs].reverse().map((breadcrumb) => breadcrumb.text as string); + + chrome.docTitle.change(docTitle); + chrome.setBreadcrumbs(breadcrumbs); }, [appLinkProps, appTitle, chrome, extraCrumbs, observabilityLinkProps]); }; diff --git a/x-pack/plugins/infra/public/hooks/use_document_title.tsx b/x-pack/plugins/infra/public/hooks/use_document_title.tsx new file mode 100644 index 000000000000..82fb5a669eb9 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_document_title.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChromeBreadcrumb } from '@kbn/core/public'; +import { useEffect } from 'react'; +import { observabilityTitle } from '../translations'; +import { useKibanaContextForPlugin } from './use_kibana'; + +export const useDocumentTitle = (extraTitles: ChromeBreadcrumb[]) => { + const { + services: { chrome }, + } = useKibanaContextForPlugin(); + + useEffect(() => { + const docTitle = [{ text: observabilityTitle }, ...extraTitles] + .reverse() + .map((breadcrumb) => breadcrumb.text as string); + + chrome.docTitle.change(docTitle); + }, [chrome, extraTitles]); +}; diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index e42cfc2d7c54..8acd4004603e 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -12,7 +12,6 @@ import { Route, Switch } from 'react-router-dom'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-plugin/public'; import { AlertDropdown } from '../../alerting/log_threshold'; -import { DocumentTitle } from '../../components/document_title'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; @@ -62,8 +61,6 @@ export const LogsPageContent: React.FunctionComponent = () => { return ( <> - - {setHeaderActionMenu && theme$ && ( diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx index bb8e4307fe3b..19f098a6721b 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx @@ -10,7 +10,6 @@ import React from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; import { StreamPageContent } from './page_content'; -import { StreamPageHeader } from './page_header'; import { LogsPageProviders } from './page_providers'; import { streamTitle } from '../../../translations'; @@ -26,7 +25,6 @@ export const StreamPage = () => { return ( - diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx deleted file mode 100644 index f6c4ad8c8c13..000000000000 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import React from 'react'; - -import { DocumentTitle } from '../../../components/document_title'; - -export const StreamPageHeader = () => { - return ( - <> - - i18n.translate('xpack.infra.logs.streamPage.documentTitle', { - defaultMessage: '{previousTitle} | Stream', - values: { - previousTitle, - }, - }) - } - /> - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx index 5c2fed9753b8..a5dfd7f2ddd0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx @@ -6,13 +6,10 @@ */ import { EuiErrorBoundary } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import React from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { DocumentTitle } from '../../../components/document_title'; - import { SourceErrorPage } from '../../../components/source_error_page'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; @@ -42,16 +39,6 @@ export const HostsPage = () => { ]); return ( - - i18n.translate('xpack.infra.infrastructureHostsPage.documentTitle', { - defaultMessage: '{previousTitle} | Hosts', - values: { - previousTitle, - }, - }) - } - /> {isLoading && !source ? ( ) : metricIndicesExist && source ? ( diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 9c02424aac94..691069a978e8 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -15,7 +15,6 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal } from '@kbn/observability-plugin/public'; import { useLinkProps } from '@kbn/observability-plugin/public'; import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources'; -import { DocumentTitle } from '../../components/document_title'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { @@ -73,12 +72,6 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { - - { return ( - - i18n.translate('xpack.infra.infrastructureSnapshotPage.documentTitle', { - defaultMessage: '{previousTitle} | Inventory', - values: { - previousTitle, - }, - }) - } - /> {isLoading && !source ? ( ) : metricIndicesExist ? ( diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.test.tsx new file mode 100644 index 000000000000..25ae3b3717bd --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.test.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; + +import { PageError } from './page_error'; +import { errorTitle } from '../../../../translations'; +import { InfraHttpError } from '../../../../types'; +import { useDocumentTitle } from '../../../../hooks/use_document_title'; +import { I18nProvider } from '@kbn/i18n-react'; + +jest.mock('../../../../hooks/use_document_title', () => ({ + useDocumentTitle: jest.fn(), +})); + +const renderErrorPage = () => + render( + + + + ); + +describe('PageError component', () => { + it('renders correctly and set title', () => { + const { getByText } = renderErrorPage(); + expect(useDocumentTitle).toHaveBeenCalledWith([{ text: `${errorTitle}` }]); + + expect(getByText('Error Message')).toBeInTheDocument(); + expect(getByText('Please click the back button and try again.')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx index a6665e0d244f..b4cdb47399e9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx @@ -6,11 +6,11 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; +import { useDocumentTitle } from '../../../../hooks/use_document_title'; import { InvalidNodeError } from './invalid_node'; -import { DocumentTitle } from '../../../../components/document_title'; import { ErrorPageBody } from '../../../error'; import { InfraHttpError } from '../../../../types'; +import { errorTitle } from '../../../../translations'; interface Props { name: string; @@ -18,18 +18,10 @@ interface Props { } export const PageError = ({ error, name }: Props) => { + useDocumentTitle([{ text: errorTitle }]); + return ( <> - - i18n.translate('xpack.infra.metricDetailPage.documentTitleError', { - defaultMessage: '{previousTitle} | Uh oh', - values: { - previousTitle, - }, - }) - } - /> {error.body?.statusCode === 404 ? ( ) : ( diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx index 823b9d703f50..9b92901976bf 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { EuiTheme, withTheme } from '@kbn/kibana-react-plugin/common'; import { useLinkProps } from '@kbn/observability-plugin/public'; -import { DocumentTitle } from '../../../components/document_title'; import { withMetricPageProviders } from './page_providers'; import { useMetadata } from './hooks/use_metadata'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; @@ -100,14 +99,6 @@ export const MetricDetail = withMetricPageProviders( return ( <> - {metadata ? ( - - i18n.translate('xpack.infra.infrastructureMetricsExplorerPage.documentTitle', { - defaultMessage: '{previousTitle} | Metrics Explorer', - values: { - previousTitle, - }, - }) - } - /> { const esArchiver = getService('esArchiver'); + const browser = getService('browser'); const retry = getService('retry'); - const pageObjects = getPageObjects(['common', 'infraHome', 'infraSavedViews']); + const pageObjects = getPageObjects(['common', 'header', 'infraHome', 'infraSavedViews']); const kibanaServer = getService('kibanaServer'); describe('Home page', function () { @@ -34,6 +35,22 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.common.navigateToApp('infraOps'); await pageObjects.infraHome.getNoMetricsIndicesPrompt(); }); + + it('renders the correct error page title', async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'infraOps', + '/detail/host/test', + '', + { + ensureCurrentUrl: false, + } + ); + await pageObjects.infraHome.waitForLoading(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + const documentTitle = await browser.getTitle(); + expect(documentTitle).to.contain('Uh oh - Observability - Elastic'); + }); }); describe('with metrics present', () => { @@ -47,6 +64,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs') ); + it('renders the correct page title', async () => { + await pageObjects.header.waitUntilLoadingHasFinished(); + + const documentTitle = await browser.getTitle(); + expect(documentTitle).to.contain('Inventory - Infrastructure - Observability - Elastic'); + }); + it('renders an empty data prompt for dates with no data', async () => { await pageObjects.infraHome.goToTime(DATE_WITHOUT_DATA); await pageObjects.infraHome.getNoMetricsDataPrompt(); diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index ebfcb740961b..05eccc8e57eb 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -42,6 +42,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.tryForTime(5000, async () => { const currentUrl = await browser.getCurrentUrl(); const parsedUrl = new URL(currentUrl); + const documentTitle = await browser.getTitle(); expect(parsedUrl.pathname).to.be('/app/logs/stream'); expect(parsedUrl.searchParams.get('logFilter')).to.be( @@ -51,6 +52,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { `(end:'${endDate}',position:(tiebreaker:0,time:${timestamp}),start:'${startDate}',streamLive:!f)` ); expect(parsedUrl.searchParams.get('sourceId')).to.be('default'); + expect(documentTitle).to.contain('Stream - Logs - Observability - Elastic'); }); }); }); diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts index 56eed5ec4b63..38cc795034a2 100644 --- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts @@ -16,6 +16,7 @@ const COMMON_REQUEST_HEADERS = { export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); + const browser = getService('browser'); const logsUi = getService('logsUi'); const infraSourceConfigurationForm = getService('infraSourceConfigurationForm'); const pageObjects = getPageObjects(['common', 'header', 'infraLogs']); @@ -49,6 +50,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); }); + it('renders the correct page title', async () => { + await pageObjects.infraLogs.navigateToTab('settings'); + + await pageObjects.header.waitUntilLoadingHasFinished(); + const documentTitle = await browser.getTitle(); + + expect(documentTitle).to.contain('Settings - Logs - Observability - Elastic'); + }); + it('can change the log indices to a pattern that matches nothing', async () => { await pageObjects.infraLogs.navigateToTab('settings'); diff --git a/x-pack/test/functional/apps/infra/metrics_explorer.ts b/x-pack/test/functional/apps/infra/metrics_explorer.ts index fc620d9ba566..4d6859a4e99e 100644 --- a/x-pack/test/functional/apps/infra/metrics_explorer.ts +++ b/x-pack/test/functional/apps/infra/metrics_explorer.ts @@ -16,6 +16,7 @@ const timepickerFormat = 'MMM D, YYYY @ HH:mm:ss.SSS'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const browser = getService('browser'); const pageObjects = getPageObjects([ 'common', 'infraHome', @@ -42,6 +43,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); + it('should render the correct page title', async () => { + const documentTitle = await browser.getTitle(); + expect(documentTitle).to.contain( + 'Metrics Explorer - Infrastructure - Observability - Elastic' + ); + }); + it('should have three metrics by default', async () => { const metrics = await pageObjects.infraMetricsExplorer.getMetrics(); expect(metrics.length).to.equal(3); From 5f057ff610139a88d660e48e41bd5c2648140800 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Wed, 28 Sep 2022 13:49:07 +0200 Subject: [PATCH 059/185] Add enrichment event log time (#141433) * Add enrichment event log time * fix types * Fix test * Add avg field * Fix enrichments event log * Add telemetry * Update schema Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/event_log/README.md | 1 + .../plugins/event_log/generated/mappings.json | 3 + x-pack/plugins/event_log/generated/schemas.ts | 1 + x-pack/plugins/event_log/scripts/mappings.js | 3 + .../model/execution_metrics.ts | 9 + .../client_for_executors/client.ts | 1 + .../client_for_executors/client_interface.ts | 1 + .../saved_objects_type.ts | 3 + .../create_security_rule_type_wrapper.ts | 5 + .../factories/bulk_create_factory.ts | 24 +- .../lib/detection_engine/rule_types/types.ts | 1 + .../rule_types/utils/index.ts | 1 + .../threat_mapping/create_threat_signals.ts | 1 + .../signals/threat_mapping/utils.test.ts | 38 ++ .../signals/threat_mapping/utils.ts | 4 + .../lib/detection_engine/signals/types.ts | 1 + .../detection_engine/signals/utils.test.ts | 13 + .../lib/detection_engine/signals/utils.ts | 7 + .../server/usage/collector.ts | 252 ++++++++++ .../detections/rules/get_initial_usage.ts | 1 + .../detections/rules/get_metrics.mocks.ts | 414 ++++++++++++++++ .../server/usage/detections/rules/types.ts | 1 + ...event_log_agg_by_rule_type_metrics.test.ts | 15 + .../get_event_log_agg_by_rule_type_metrics.ts | 15 + ...vent_log_agg_by_rule_types_metrics.test.ts | 60 +++ .../get_event_log_agg_by_statuses.test.ts | 60 +++ .../transform_single_rule_metric.test.ts | 14 + .../utils/transform_single_rule_metric.ts | 5 + .../security_solution/server/usage/types.ts | 9 + .../schema/xpack_plugins.json | 468 ++++++++++++++++-- 30 files changed, 1394 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/event_log/README.md b/x-pack/plugins/event_log/README.md index c1d5869e7ed4..05b05946b652 100644 --- a/x-pack/plugins/event_log/README.md +++ b/x-pack/plugins/event_log/README.md @@ -159,6 +159,7 @@ Below is a document in the expected structure, with descriptions of the fields: es_search_duration_ms: "total time spent performing ES searches as measured by Elasticsearch", total_search_duration_ms: "total time spent performing ES searches as measured by Kibana; includes network latency and time spent serializing/deserializing request/response", total_indexing_duration_ms: "total time spent indexing documents during current rule execution cycle", + total_enrichment_duration_ms: "total time spent enriching documents during current rule execution cycle", execution_gap_duration_s: "duration in seconds of execution gap" } } diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index db6719fed996..4756b4f2e553 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -347,6 +347,9 @@ }, "total_run_duration_ms": { "type": "long" + }, + "total_enrichment_duration_ms": { + "type": "long" } } } diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index b5557f64e9ac..523ad683eabf 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -150,6 +150,7 @@ export const EventSchema = schema.maybe( claim_to_start_duration_ms: ecsStringOrNumber(), prepare_rule_duration_ms: ecsStringOrNumber(), total_run_duration_ms: ecsStringOrNumber(), + total_enrichment_duration_ms: ecsStringOrNumber(), }) ), }) diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 98050b655721..65c9220fb535 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -130,6 +130,9 @@ exports.EcsCustomPropertyMappings = { total_run_duration_ms: { type: 'long', }, + total_enrichment_duration_ms: { + type: 'long', + }, }, }, }, diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring/model/execution_metrics.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring/model/execution_metrics.ts index c6bb71970e59..b15c76119e44 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring/model/execution_metrics.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring/model/execution_metrics.ts @@ -12,8 +12,17 @@ export type DurationMetric = t.TypeOf; export const DurationMetric = PositiveInteger; export type RuleExecutionMetrics = t.TypeOf; + +/** + @property total_search_duration_ms - "total time spent performing ES searches as measured by Kibana; + includes network latency and time spent serializing/deserializing request/response", + @property total_indexing_duration_ms - "total time spent indexing documents during current rule execution cycle", + @property total_enrichment_duration_ms - total time spent enriching documents during current rule execution cycle + @property execution_gap_duration_s - "duration in seconds of execution gap" +*/ export const RuleExecutionMetrics = t.partial({ total_search_duration_ms: DurationMetric, total_indexing_duration_ms: DurationMetric, + total_enrichment_duration_ms: DurationMetric, execution_gap_duration_s: DurationMetric, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts index 4116848b1ffc..3c712847851f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts @@ -222,6 +222,7 @@ const normalizeStatusChangeArgs = (args: StatusChangeArgs): NormalizedStatusChan ? { total_search_duration_ms: normalizeDurations(metrics.searchDurations), total_indexing_duration_ms: normalizeDurations(metrics.indexingDurations), + total_enrichment_duration_ms: normalizeDurations(metrics.enrichmentDurations), execution_gap_duration_s: normalizeGap(metrics.executionGap), } : undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts index 22392e699fce..5e48a43e949c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts @@ -115,5 +115,6 @@ export interface StatusChangeArgs { export interface MetricsArgs { searchDurations?: string[]; indexingDurations?: string[]; + enrichmentDurations?: string[]; executionGap?: Duration; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/execution_saved_object/saved_objects_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/execution_saved_object/saved_objects_type.ts index ac3b28e87e0d..1e0a2e74cadc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/execution_saved_object/saved_objects_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/execution_saved_object/saved_objects_type.ts @@ -58,6 +58,9 @@ const ruleExecutionMappings: SavedObjectsType['mappings'] = { total_indexing_duration_ms: { type: 'long', }, + total_enrichment_duration_ms: { + type: 'long', + }, execution_gap_duration_s: { type: 'long', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index ae3f310d841c..05813ed1ee29 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -343,6 +343,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const warningMessages = result.warningMessages.concat(runResult.warningMessages); result = { bulkCreateTimes: result.bulkCreateTimes.concat(runResult.bulkCreateTimes), + enrichmentTimes: result.enrichmentTimes.concat(runResult.enrichmentTimes), createdSignals, createdSignalsCount: createdSignals.length, errors: result.errors.concat(runResult.errors), @@ -358,6 +359,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = } else { result = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignals: [], createdSignalsCount: 0, errors: [], @@ -434,6 +436,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = metrics: { searchDurations: result.searchAfterTimes, indexingDurations: result.bulkCreateTimes, + enrichmentDurations: result.enrichmentTimes, }, }); } @@ -452,6 +455,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = metrics: { searchDurations: result.searchAfterTimes, indexingDurations: result.bulkCreateTimes, + enrichmentDurations: result.enrichmentTimes, }, }); } @@ -464,6 +468,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = metrics: { searchDurations: result.searchAfterTimes, indexingDurations: result.bulkCreateTimes, + enrichmentDurations: result.enrichmentTimes, }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts index 81f15364ff12..95bc32571f6e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts @@ -21,6 +21,7 @@ import type { export interface GenericBulkCreateResponse { success: boolean; bulkCreateDuration: string; + enrichmentDuration: string; createdItemsCount: number; createdItems: Array & { _id: string; _index: string }>; errors: string[]; @@ -45,6 +46,7 @@ export const bulkCreateFactory = return { errors: [], success: true, + enrichmentDuration: '0', bulkCreateDuration: '0', createdItemsCount: 0, createdItems: [], @@ -54,6 +56,24 @@ export const bulkCreateFactory = const start = performance.now(); + let enrichmentsTimeStart = 0; + let enrichmentsTimeFinish = 0; + let enrichAlertsWrapper: typeof enrichAlerts; + if (enrichAlerts) { + enrichAlertsWrapper = async (alerts, params) => { + enrichmentsTimeStart = performance.now(); + try { + const enrichedAlerts = await enrichAlerts(alerts, params); + return enrichedAlerts; + } catch (error) { + ruleExecutionLogger.error(`Enrichments failed ${error}`); + throw error; + } finally { + enrichmentsTimeFinish = performance.now(); + } + }; + } + const { createdAlerts, errors, alertsWereTruncated } = await alertWithPersistence( wrappedDocs.map((doc) => ({ _id: doc._id, @@ -62,7 +82,7 @@ export const bulkCreateFactory = })), refreshForBulkCreate, maxAlerts, - enrichAlerts + enrichAlertsWrapper ); const end = performance.now(); @@ -78,6 +98,7 @@ export const bulkCreateFactory = return { errors: Object.keys(errors), success: false, + enrichmentDuration: makeFloatString(enrichmentsTimeFinish - enrichmentsTimeStart), bulkCreateDuration: makeFloatString(end - start), createdItemsCount: createdAlerts.length, createdItems: createdAlerts, @@ -88,6 +109,7 @@ export const bulkCreateFactory = errors: [], success: true, bulkCreateDuration: makeFloatString(end - start), + enrichmentDuration: makeFloatString(enrichmentsTimeFinish - enrichmentsTimeStart), createdItemsCount: createdAlerts.length, createdItems: createdAlerts, alertsWereTruncated, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 403d6542f4f0..e1af3077ec3e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -42,6 +42,7 @@ import type { IRuleExecutionLogForExecutors, IRuleExecutionLogService } from '.. export interface SecurityAlertTypeReturnValue { bulkCreateTimes: string[]; + enrichmentTimes: string[]; createdSignalsCount: number; createdSignals: unknown[]; errors: string[]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/index.ts index 5d2dcd4a4b3d..8f23da386f5c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/index.ts @@ -10,6 +10,7 @@ import type { SecurityAlertTypeReturnValue } from '../types'; export const createResultObject = (state: TState) => { const result: SecurityAlertTypeReturnValue = { + enrichmentTimes: [], bulkCreateTimes: [], createdSignalsCount: 0, createdSignals: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index d80e5cba026b..0ab6ec2a01dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -65,6 +65,7 @@ export const createThreatSignals = async ({ let results: SearchAfterAndBulkCreateReturnType = { success: true, warning: false, + enrichmentTimes: [], bulkCreateTimes: [], searchAfterTimes: [], lastLookBackDate: null, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts index 981868589e4a..0bcdc8450a83 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts @@ -55,6 +55,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -67,6 +68,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -83,6 +85,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -95,6 +98,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -111,6 +115,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -123,6 +128,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -139,6 +145,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -151,6 +158,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -162,6 +170,7 @@ describe('utils', () => { expect.objectContaining({ searchAfterTimes: ['60'], bulkCreateTimes: ['50'], + enrichmentTimes: ['6'], }) ); }); @@ -172,6 +181,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -184,6 +194,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -296,6 +307,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -307,6 +319,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['30'], // max value from existingResult.searchAfterTimes bulkCreateTimes: ['25'], // max value from existingResult.bulkCreateTimes + enrichmentTimes: ['3'], // max value from existingResult.enrichmentTimes lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -323,6 +336,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -334,6 +348,7 @@ describe('utils', () => { warning: false, searchAfterTimes: [], bulkCreateTimes: [], + enrichmentTimes: [], lastLookBackDate: undefined, createdSignalsCount: 0, createdSignals: [], @@ -345,6 +360,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['30'], // max value from existingResult.searchAfterTimes bulkCreateTimes: ['25'], // max value from existingResult.bulkCreateTimes + enrichmentTimes: ['3'], // max value from existingResult.enrichmentTimes lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -362,6 +378,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], // max is 30 bulkCreateTimes: ['5', '15', '25'], // max is 25 + enrichmentTimes: ['1', '2', '3'], // max is 3 lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -373,6 +390,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), @@ -384,6 +402,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['40', '5', '15'], bulkCreateTimes: ['50', '5', '15'], + enrichmentTimes: ['4', '2', '3'], lastLookBackDate: new Date('2020-09-16T04:34:32.390Z'), createdSignalsCount: 8, createdSignals: Array(8).fill(sampleSignalHit()), @@ -396,6 +415,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['70'], // max value between newResult1 and newResult2 + max array value of existingResult (40 + 30 = 70) bulkCreateTimes: ['75'], // max value between newResult1 and newResult2 + max array value of existingResult (50 + 25 = 75) + enrichmentTimes: ['7'], // max value between newResult1 and newResult2 + max array value of existingResult (4 + 3 = 7) lastLookBackDate: new Date('2020-09-16T04:34:32.390Z'), // max lastLookBackDate createdSignalsCount: 16, // all the signals counted together (8 + 5 + 3) createdSignals: Array(16).fill(sampleSignalHit()), @@ -413,6 +433,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], // max is 30 bulkCreateTimes: ['5', '15', '25'], // max is 25 + enrichmentTimes: ['1', '2', '3'], // max is 3 lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -424,6 +445,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), @@ -435,6 +457,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['40', '5', '15'], bulkCreateTimes: ['50', '5', '15'], + enrichmentTimes: ['5', '2', '3'], lastLookBackDate: new Date('2020-09-16T04:34:32.390Z'), createdSignalsCount: 8, createdSignals: Array(8).fill(sampleSignalHit()), @@ -447,6 +470,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['70'], // max value between newResult1 and newResult2 + max array value of existingResult (40 + 30 = 70) bulkCreateTimes: ['75'], // max value between newResult1 and newResult2 + max array value of existingResult (50 + 25 = 75) + enrichmentTimes: ['8'], // max value between newResult1 and newResult2 + max array value of existingResult (50 + 3 = 8) lastLookBackDate: new Date('2020-09-16T04:34:32.390Z'), // max lastLookBackDate createdSignalsCount: 16, // all the signals counted together (8 + 5 + 3) createdSignals: Array(16).fill(sampleSignalHit()), @@ -464,6 +488,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], // max is 30 bulkCreateTimes: ['5', '15', '25'], // max is 25 + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -475,6 +500,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), @@ -486,6 +512,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['40', '5', '15'], bulkCreateTimes: ['50', '5', '15'], + enrichmentTimes: ['5', '2', '3'], lastLookBackDate: null, createdSignalsCount: 8, createdSignals: Array(8).fill(sampleSignalHit()), @@ -498,6 +525,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['70'], // max value between newResult1 and newResult2 + max array value of existingResult (40 + 30 = 70) bulkCreateTimes: ['75'], // max value between newResult1 and newResult2 + max array value of existingResult (50 + 25 = 75) + enrichmentTimes: ['8'], // max value between newResult1 and newResult2 + max array value of existingResult (5 + 3 = 8) lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), // max lastLookBackDate createdSignalsCount: 16, // all the signals counted together (8 + 5 + 3) createdSignals: Array(16).fill(sampleSignalHit()), @@ -515,6 +543,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -527,6 +556,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['5', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -543,6 +573,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -555,6 +586,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -571,6 +603,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -583,6 +616,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -599,6 +633,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -611,6 +646,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -632,6 +668,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: undefined, createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), @@ -644,6 +681,7 @@ describe('utils', () => { warning: false, searchAfterTimes: ['10', '20', '30'], bulkCreateTimes: ['5', '15', '25'], + enrichmentTimes: ['1', '2', '3'], lastLookBackDate: new Date('2020-09-16T03:34:32.390Z'), createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts index bfba9a6fd22a..a73ead0bf946 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts @@ -70,6 +70,7 @@ export const combineResults = ( ): SearchAfterAndBulkCreateReturnType => ({ success: currentResult.success === false ? false : newResult.success, warning: currentResult.warning || newResult.warning, + enrichmentTimes: calculateAdditiveMax(currentResult.enrichmentTimes, newResult.enrichmentTimes), bulkCreateTimes: calculateAdditiveMax(currentResult.bulkCreateTimes, newResult.bulkCreateTimes), searchAfterTimes: calculateAdditiveMax( currentResult.searchAfterTimes, @@ -94,6 +95,7 @@ export const combineConcurrentResults = ( const maxedNewResult = newResult.reduce( (accum, item) => { const maxSearchAfterTime = calculateMax(accum.searchAfterTimes, item.searchAfterTimes); + const maxEnrichmentTimes = calculateMax(accum.enrichmentTimes, item.enrichmentTimes); const maxBulkCreateTimes = calculateMax(accum.bulkCreateTimes, item.bulkCreateTimes); const lastLookBackDate = calculateMaxLookBack(accum.lastLookBackDate, item.lastLookBackDate); return { @@ -101,6 +103,7 @@ export const combineConcurrentResults = ( warning: accum.warning || item.warning, searchAfterTimes: [maxSearchAfterTime], bulkCreateTimes: [maxBulkCreateTimes], + enrichmentTimes: [maxEnrichmentTimes], lastLookBackDate, createdSignalsCount: accum.createdSignalsCount + item.createdSignalsCount, createdSignals: [...accum.createdSignals, ...item.createdSignals], @@ -113,6 +116,7 @@ export const combineConcurrentResults = ( warning: false, searchAfterTimes: [], bulkCreateTimes: [], + enrichmentTimes: [], lastLookBackDate: undefined, createdSignalsCount: 0, createdSignals: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 93ddadf826d7..f786a28b5004 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -286,6 +286,7 @@ export interface SearchAfterAndBulkCreateReturnType { success: boolean; warning: boolean; searchAfterTimes: string[]; + enrichmentTimes: string[]; bulkCreateTimes: string[]; lastLookBackDate: Date | null | undefined; createdSignalsCount: number; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index d5603130400c..d80ed256a0de 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -953,6 +953,7 @@ describe('utils', () => { }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignalsCount: 0, createdSignals: [], errors: [], @@ -973,6 +974,7 @@ describe('utils', () => { }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignalsCount: 0, createdSignals: [], errors: [], @@ -1291,6 +1293,7 @@ describe('utils', () => { const searchAfterReturnType = createSearchAfterReturnType(); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignalsCount: 0, createdSignals: [], errors: [], @@ -1306,6 +1309,7 @@ describe('utils', () => { test('createSearchAfterReturnType can override all values', () => { const searchAfterReturnType = createSearchAfterReturnType({ bulkCreateTimes: ['123'], + enrichmentTimes: [], createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), errors: ['error 1'], @@ -1317,6 +1321,7 @@ describe('utils', () => { }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: ['123'], + enrichmentTimes: [], createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), errors: ['error 1'], @@ -1337,6 +1342,7 @@ describe('utils', () => { }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignalsCount: 5, createdSignals: Array(5).fill(sampleSignalHit()), errors: ['error 1'], @@ -1355,6 +1361,7 @@ describe('utils', () => { const merged = mergeReturns([createSearchAfterReturnType(), createSearchAfterReturnType()]); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], + enrichmentTimes: [], createdSignalsCount: 0, createdSignals: [], errors: [], @@ -1411,6 +1418,7 @@ describe('utils', () => { const merged = mergeReturns([ createSearchAfterReturnType({ bulkCreateTimes: ['123'], + enrichmentTimes: [], createdSignalsCount: 3, createdSignals: Array(3).fill(sampleSignalHit()), errors: ['error 1', 'error 2'], @@ -1421,6 +1429,7 @@ describe('utils', () => { }), createSearchAfterReturnType({ bulkCreateTimes: ['456'], + enrichmentTimes: [], createdSignalsCount: 2, createdSignals: Array(2).fill(sampleSignalHit()), errors: ['error 3'], @@ -1433,6 +1442,7 @@ describe('utils', () => { ]); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: ['123', '456'], // concatenates the prev and next together + enrichmentTimes: [], createdSignalsCount: 5, // Adds the 3 and 2 together createdSignals: Array(5).fill(sampleSignalHit()), errors: ['error 1', 'error 2', 'error 3'], // concatenates the prev and next together @@ -1452,6 +1462,7 @@ describe('utils', () => { const next: GenericBulkCreateResponse = { success: false, bulkCreateDuration: '100', + enrichmentDuration: '0', createdItemsCount: 1, createdItems: [], errors: ['new error'], @@ -1469,6 +1480,7 @@ describe('utils', () => { const next: GenericBulkCreateResponse = { success: true, bulkCreateDuration: '0', + enrichmentDuration: '0', createdItemsCount: 0, createdItems: [], errors: ['error 1'], @@ -1484,6 +1496,7 @@ describe('utils', () => { const next: GenericBulkCreateResponse = { success: true, bulkCreateDuration: '0', + enrichmentDuration: '0', createdItemsCount: 0, createdItems: [], errors: ['error 2'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 6030400da2b7..24b5b068d568 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -649,6 +649,7 @@ export const createSearchAfterReturnType = ({ success, warning, searchAfterTimes, + enrichmentTimes, bulkCreateTimes, lastLookBackDate, createdSignalsCount, @@ -659,6 +660,7 @@ export const createSearchAfterReturnType = ({ success?: boolean | undefined; warning?: boolean; searchAfterTimes?: string[] | undefined; + enrichmentTimes?: string[] | undefined; bulkCreateTimes?: string[] | undefined; lastLookBackDate?: Date | undefined; createdSignalsCount?: number | undefined; @@ -670,6 +672,7 @@ export const createSearchAfterReturnType = ({ success: success ?? true, warning: warning ?? false, searchAfterTimes: searchAfterTimes ?? [], + enrichmentTimes: enrichmentTimes ?? [], bulkCreateTimes: bulkCreateTimes ?? [], lastLookBackDate: lastLookBackDate ?? null, createdSignalsCount: createdSignalsCount ?? 0, @@ -715,6 +718,7 @@ export const addToSearchAfterReturn = ({ current.createdSignalsCount += next.createdItemsCount; current.createdSignals.push(...next.createdItems); current.bulkCreateTimes.push(next.bulkCreateDuration); + current.enrichmentTimes.push(next.enrichmentDuration); current.errors = [...new Set([...current.errors, ...next.errors])]; }; @@ -727,6 +731,7 @@ export const mergeReturns = ( warning: existingWarning, searchAfterTimes: existingSearchAfterTimes, bulkCreateTimes: existingBulkCreateTimes, + enrichmentTimes: existingEnrichmentTimes, lastLookBackDate: existingLastLookBackDate, createdSignalsCount: existingCreatedSignalsCount, createdSignals: existingCreatedSignals, @@ -738,6 +743,7 @@ export const mergeReturns = ( success: newSuccess, warning: newWarning, searchAfterTimes: newSearchAfterTimes, + enrichmentTimes: newEnrichmentTimes, bulkCreateTimes: newBulkCreateTimes, lastLookBackDate: newLastLookBackDate, createdSignalsCount: newCreatedSignalsCount, @@ -750,6 +756,7 @@ export const mergeReturns = ( success: existingSuccess && newSuccess, warning: existingWarning || newWarning, searchAfterTimes: [...existingSearchAfterTimes, ...newSearchAfterTimes], + enrichmentTimes: [...existingEnrichmentTimes, ...newEnrichmentTimes], bulkCreateTimes: [...existingBulkCreateTimes, ...newBulkCreateTimes], lastLookBackDate: newLastLookBackDate ?? existingLastLookBackDate, createdSignalsCount: existingCreatedSignalsCount + newCreatedSignalsCount, diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index e14af8c55373..6f526051f17d 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -414,6 +414,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -500,6 +514,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -586,6 +614,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -672,6 +714,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -758,6 +814,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -844,6 +914,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -946,6 +1030,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1032,6 +1130,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1118,6 +1230,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1204,6 +1330,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1290,6 +1430,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1376,6 +1530,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1478,6 +1646,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1564,6 +1746,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1650,6 +1846,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1736,6 +1946,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1822,6 +2046,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', @@ -1908,6 +2146,20 @@ export const registerCollector: RegisterCollector = ({ _meta: { description: 'The min duration' }, }, }, + enrichment_duration: { + max: { + type: 'float', + _meta: { description: 'The max duration' }, + }, + avg: { + type: 'float', + _meta: { description: 'The avg duration' }, + }, + min: { + type: 'float', + _meta: { description: 'The min duration' }, + }, + }, gap_duration: { max: { type: 'float', diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts index eb32c58bda6c..3ad6b5740b53 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts @@ -144,6 +144,7 @@ export const getInitialSingleEventMetric = (): SingleEventMetric => ({ succeeded: 0, index_duration: getInitialMaxAvgMin(), search_duration: getInitialMaxAvgMin(), + enrichment_duration: getInitialMaxAvgMin(), gap_duration: getInitialMaxAvgMin(), gap_count: 0, }); diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/get_metrics.mocks.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/get_metrics.mocks.ts index 129d2b32d0b3..83eb7df28a02 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/get_metrics.mocks.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/get_metrics.mocks.ts @@ -155,6 +155,15 @@ export const getEventLogAllRules = (): SearchResponse ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threat_match: { @@ -2506,6 +2835,11 @@ export const getEventLogAllRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, machine_learning: { @@ -2529,6 +2863,11 @@ export const getEventLogAllRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, query: { @@ -2594,6 +2933,11 @@ export const getEventLogAllRulesResult = (): SingleEventLogStatusMetric => ({ avg: 4246.375, min: 2811, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 6, }, saved_query: { @@ -2617,6 +2961,11 @@ export const getEventLogAllRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threshold: { @@ -2640,6 +2989,11 @@ export const getEventLogAllRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, total: { @@ -2676,6 +3030,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threat_match: { @@ -2699,6 +3058,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, machine_learning: { @@ -2722,6 +3086,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, query: { @@ -2772,6 +3141,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 4141.75, min: 2811, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 4, }, saved_query: { @@ -2795,6 +3169,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threshold: { @@ -2818,6 +3197,11 @@ export const getEventLogElasticRulesResult = (): SingleEventLogStatusMetric => ( avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, total: { @@ -2854,6 +3238,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threat_match: { @@ -2877,6 +3266,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, machine_learning: { @@ -2900,6 +3294,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, query: { @@ -2940,6 +3339,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 4351, min: 3051, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 2, }, saved_query: { @@ -2963,6 +3367,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, threshold: { @@ -2986,6 +3395,11 @@ export const getEventLogCustomRulesResult = (): SingleEventLogStatusMetric => ({ avg: 0, min: 0, }, + enrichment_duration: { + max: 0, + avg: 0, + min: 0, + }, gap_count: 0, }, total: { diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts index 499c79b11fcf..84fb656f793d 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts @@ -85,6 +85,7 @@ export interface SingleEventMetric { succeeded: number; index_duration: MaxAvgMin; search_duration: MaxAvgMin; + enrichment_duration: MaxAvgMin; gap_duration: MaxAvgMin; gap_count: number; } diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.test.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.test.ts index 09a988fbf02e..693e3579d7c3 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.test.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.test.ts @@ -68,6 +68,21 @@ describe('get_event_log_agg_by_rule_type_metrics', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }); }); diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.ts index 6fe8103e29a0..957f56809d64 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_type_metrics.ts @@ -74,6 +74,21 @@ export const getEventLogAggByRuleTypeMetrics = ( field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }; }; diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_types_metrics.test.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_types_metrics.test.ts index 22261ac48812..4673ca1b6bcb 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_types_metrics.test.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_rule_types_metrics.test.ts @@ -74,6 +74,21 @@ describe('get_event_log_agg_by_rule_types_metrics', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }); @@ -139,6 +154,21 @@ describe('get_event_log_agg_by_rule_types_metrics', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }); @@ -204,6 +234,21 @@ describe('get_event_log_agg_by_rule_types_metrics', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, 'siem.indicatorRule': { @@ -263,6 +308,21 @@ describe('get_event_log_agg_by_rule_types_metrics', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }); diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_statuses.test.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_statuses.test.ts index 7d474769bd79..a87046660fe1 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_statuses.test.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/get_event_log_agg_by_statuses.test.ts @@ -137,6 +137,21 @@ describe('get_event_log_agg_by_statuses', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }, @@ -246,6 +261,21 @@ describe('get_event_log_agg_by_statuses', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }, @@ -418,6 +448,21 @@ describe('get_event_log_agg_by_statuses', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, 'siem.thresholdRule': { @@ -477,6 +522,21 @@ describe('get_event_log_agg_by_statuses', () => { field: 'kibana.alert.rule.execution.metrics.total_search_duration_ms', }, }, + maxTotalEnrichmentDuration: { + max: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + minTotalEnrichmentDuration: { + min: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, + avgTotalEnrichmentDuration: { + avg: { + field: 'kibana.alert.rule.execution.metrics.total_enrichment_duration_ms', + }, + }, }, }, }, diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.test.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.test.ts index c64f0833fe85..9c5810011a97 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.test.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.test.ts @@ -85,6 +85,15 @@ describe('transform_single_rule_metric', () => { minTotalSearchDuration: { value: 12, }, + minTotalEnrichmentDuration: { + value: 4, + }, + maxTotalEnrichmentDuration: { + value: 2, + }, + avgTotalEnrichmentDuration: { + value: 12, + }, }, }); @@ -131,6 +140,11 @@ describe('transform_single_rule_metric', () => { avg: 2, min: 9, }, + enrichment_duration: { + max: 2, + avg: 12, + min: 4, + }, gap_count: 4, }); }); diff --git a/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.ts b/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.ts index bebd867fb195..5b3b6f8b7ebb 100644 --- a/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.ts +++ b/x-pack/plugins/security_solution/server/usage/queries/utils/transform_single_rule_metric.ts @@ -52,6 +52,11 @@ export const transformSingleRuleMetric = ({ avg: singleMetric.avgTotalSearchDuration.value ?? 0.0, min: singleMetric.minTotalSearchDuration.value ?? 0.0, }, + enrichment_duration: { + max: singleMetric?.maxTotalEnrichmentDuration?.value ?? 0.0, + avg: singleMetric?.avgTotalEnrichmentDuration?.value ?? 0.0, + min: singleMetric?.minTotalEnrichmentDuration?.value ?? 0.0, + }, gap_duration: { max: singleMetric.maxGapDuration.value ?? 0.0, avg: singleMetric.avgGapDuration.value ?? 0.0, diff --git a/x-pack/plugins/security_solution/server/usage/types.ts b/x-pack/plugins/security_solution/server/usage/types.ts index fe7711196303..a6db2e3d71e9 100644 --- a/x-pack/plugins/security_solution/server/usage/types.ts +++ b/x-pack/plugins/security_solution/server/usage/types.ts @@ -121,6 +121,15 @@ export interface SingleExecutionMetricAgg { minTotalSearchDuration: { value: number | null; }; + maxTotalEnrichmentDuration: { + value: number | null; + }; + avgTotalEnrichmentDuration: { + value: number | null; + }; + minTotalEnrichmentDuration: { + value: number | null; + }; } export interface EventLogTypeStatusAggs { diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 0c389000f6c8..9a47ad9de093 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -4644,6 +4644,18 @@ "properties": { "all": { "properties": { + "total": { + "type": "long" + }, + "monthly": { + "type": "long" + }, + "weekly": { + "type": "long" + }, + "daily": { + "type": "long" + }, "assignees": { "properties": { "total": { @@ -4657,18 +4669,6 @@ } } }, - "total": { - "type": "long" - }, - "monthly": { - "type": "long" - }, - "weekly": { - "type": "long" - }, - "daily": { - "type": "long" - }, "status": { "properties": { "open": { @@ -4720,6 +4720,18 @@ }, "sec": { "properties": { + "total": { + "type": "long" + }, + "monthly": { + "type": "long" + }, + "weekly": { + "type": "long" + }, + "daily": { + "type": "long" + }, "assignees": { "properties": { "total": { @@ -4732,7 +4744,11 @@ "type": "long" } } - }, + } + } + }, + "obs": { + "properties": { "total": { "type": "long" }, @@ -4744,11 +4760,7 @@ }, "daily": { "type": "long" - } - } - }, - "obs": { - "properties": { + }, "assignees": { "properties": { "total": { @@ -4761,7 +4773,11 @@ "type": "long" } } - }, + } + } + }, + "main": { + "properties": { "total": { "type": "long" }, @@ -4773,11 +4789,7 @@ }, "daily": { "type": "long" - } - } - }, - "main": { - "properties": { + }, "assignees": { "properties": { "total": { @@ -4790,18 +4802,6 @@ "type": "long" } } - }, - "total": { - "type": "long" - }, - "monthly": { - "type": "long" - }, - "weekly": { - "type": "long" - }, - "daily": { - "type": "long" } } } @@ -10029,6 +10029,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10161,6 +10183,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10293,6 +10337,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10425,6 +10491,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10557,6 +10645,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10689,6 +10799,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10847,6 +10979,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -10979,6 +11133,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11111,6 +11287,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11243,6 +11441,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11375,6 +11595,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11507,6 +11749,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11665,6 +11929,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11797,6 +12083,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -11929,6 +12237,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -12061,6 +12391,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -12193,6 +12545,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { @@ -12325,6 +12699,28 @@ } } }, + "enrichment_duration": { + "properties": { + "max": { + "type": "float", + "_meta": { + "description": "The max duration" + } + }, + "avg": { + "type": "float", + "_meta": { + "description": "The avg duration" + } + }, + "min": { + "type": "float", + "_meta": { + "description": "The min duration" + } + } + } + }, "gap_duration": { "properties": { "max": { From 4c12ff3fddf6edab74147ce9a714bc1e5e91c65e Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 28 Sep 2022 08:11:57 -0400 Subject: [PATCH 060/185] chore(slo): remove space slo resources (#141662) --- .../observability/server/assets/constants.ts | 8 +--- x-pack/plugins/observability/server/plugin.ts | 2 - .../server/routes/register_routes.ts | 4 -- .../observability/server/routes/slo/route.ts | 22 ++------- .../server/services/slo/delete_slo.test.ts | 1 + .../server/services/slo/delete_slo.ts | 3 ++ .../services/slo/resource_installer.test.ts | 19 ++------ .../server/services/slo/resource_installer.ts | 23 ++++------ .../apm_transaction_duration.test.ts.snap | 4 +- .../apm_transaction_error_rate.test.ts.snap | 4 +- .../apm_transaction_duration.test.ts | 4 +- .../apm_transaction_duration.ts | 14 +++--- .../apm_transaction_error_rate.test.ts | 6 +-- .../apm_transaction_error_rate.ts | 14 +++--- .../transform_generator.ts | 2 +- .../services/slo/transform_manager.test.ts | 46 +++---------------- .../server/services/slo/transform_manager.ts | 5 +- 17 files changed, 58 insertions(+), 123 deletions(-) diff --git a/x-pack/plugins/observability/server/assets/constants.ts b/x-pack/plugins/observability/server/assets/constants.ts index 4c0cc0e2e6f8..182ca89712dc 100644 --- a/x-pack/plugins/observability/server/assets/constants.ts +++ b/x-pack/plugins/observability/server/assets/constants.ts @@ -9,11 +9,7 @@ export const SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME = 'slo-observability.sli-mappi export const SLO_COMPONENT_TEMPLATE_SETTINGS_NAME = 'slo-observability.sli-settings'; export const SLO_INDEX_TEMPLATE_NAME = 'slo-observability.sli'; export const SLO_RESOURCES_VERSION = 1; - -export const getSLOIngestPipelineName = (spaceId: string) => - `${SLO_INDEX_TEMPLATE_NAME}.monthly-${spaceId}`; - -export const getSLODestinationIndexName = (spaceId: string) => - `${SLO_INDEX_TEMPLATE_NAME}-v${SLO_RESOURCES_VERSION}-${spaceId}`; +export const SLO_INGEST_PIPELINE_NAME = `${SLO_INDEX_TEMPLATE_NAME}.monthly`; +export const SLO_DESTINATION_INDEX_NAME = `${SLO_INDEX_TEMPLATE_NAME}-v${SLO_RESOURCES_VERSION}`; export const getSLOTransformId = (sloId: string) => `slo-${sloId}`; diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 1d9d3cbf455f..ff5fd246bea1 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -144,7 +144,6 @@ export class ObservabilityPlugin implements Plugin { const start = () => core.getStartServices().then(([coreStart]) => coreStart); - const { spacesService } = plugins.spaces; const { ruleDataService } = plugins.ruleRegistry; registerRoutes({ @@ -155,7 +154,6 @@ export class ObservabilityPlugin implements Plugin { logger: this.initContext.logger.get(), repository: getGlobalObservabilityServerRouteRepository(config), ruleDataService, - spacesService, }); return { diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index e0e9e94f5cb2..1c45eb600147 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -14,7 +14,6 @@ import { CoreSetup, CoreStart, Logger, RouteRegistrar } from '@kbn/core/server'; import Boom from '@hapi/boom'; import { errors } from '@elastic/elasticsearch'; import { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; -import { SpacesServiceStart } from '@kbn/spaces-plugin/server'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; import { getHTTPResponseCode, ObservabilityError } from '../errors'; @@ -24,7 +23,6 @@ export function registerRoutes({ core, logger, ruleDataService, - spacesService, }: { core: { setup: CoreSetup; @@ -33,7 +31,6 @@ export function registerRoutes({ repository: AbstractObservabilityServerRouteRepository; logger: Logger; ruleDataService: RuleDataPluginService; - spacesService: SpacesServiceStart; }) { const routes = Object.values(repository); @@ -67,7 +64,6 @@ export function registerRoutes({ logger, params: decodedParams, ruleDataService, - spacesService, })) as any; if (data === undefined) { diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index 3f04d5e0b13c..63ef4e1e07e6 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -38,19 +38,13 @@ const createSLORoute = createObservabilityServerRoute({ tags: [], }, params: createSLOParamsSchema, - handler: async ({ context, request, params, logger, spacesService }) => { + handler: async ({ context, params, logger }) => { const esClient = (await context.core).elasticsearch.client.asCurrentUser; const soClient = (await context.core).savedObjects.client; - const spaceId = spacesService.getSpaceId(request); - const resourceInstaller = new DefaultResourceInstaller(esClient, logger, spaceId); + const resourceInstaller = new DefaultResourceInstaller(esClient, logger); const repository = new KibanaSavedObjectsSLORepository(soClient); - const transformManager = new DefaultTransformManager( - transformGenerators, - esClient, - logger, - spaceId - ); + const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); const createSLO = new CreateSLO(resourceInstaller, repository, transformManager); const response = await createSLO.execute(params.body); @@ -65,18 +59,12 @@ const deleteSLORoute = createObservabilityServerRoute({ tags: [], }, params: deleteSLOParamsSchema, - handler: async ({ context, request, params, logger, spacesService }) => { + handler: async ({ context, params, logger }) => { const esClient = (await context.core).elasticsearch.client.asCurrentUser; const soClient = (await context.core).savedObjects.client; - const spaceId = spacesService.getSpaceId(request); const repository = new KibanaSavedObjectsSLORepository(soClient); - const transformManager = new DefaultTransformManager( - transformGenerators, - esClient, - logger, - spaceId - ); + const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); const deleteSLO = new DeleteSLO(repository, transformManager, esClient); diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts index b1a68b9c04ae..2e43c81f6d38 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts @@ -33,6 +33,7 @@ describe('DeleteSLO', () => { await deleteSLO.execute(slo.id); + expect(mockRepository.findById).toHaveBeenCalledWith(slo.id); expect(mockTransformManager.stop).toHaveBeenCalledWith(getSLOTransformId(slo.id)); expect(mockTransformManager.uninstall).toHaveBeenCalledWith(getSLOTransformId(slo.id)); expect(mockEsClient.deleteByQuery).toHaveBeenCalledWith( diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.ts index 59e5df8a5975..a7d931174a59 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.ts @@ -19,6 +19,9 @@ export class DeleteSLO { ) {} public async execute(sloId: string): Promise { + // ensure the slo exists on the request's space. + await this.repository.findById(sloId); + const sloTransformId = getSLOTransformId(sloId); await this.transformManager.stop(sloTransformId); await this.transformManager.uninstall(sloTransformId); diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts index f8746f38cd24..90749176513d 100644 --- a/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts @@ -9,7 +9,7 @@ import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/typesW import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { loggerMock } from '@kbn/logging-mocks'; import { - getSLOIngestPipelineName, + SLO_INGEST_PIPELINE_NAME, SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, SLO_INDEX_TEMPLATE_NAME, @@ -17,17 +17,12 @@ import { } from '../../assets/constants'; import { DefaultResourceInstaller } from './resource_installer'; -const SPACE_ID = 'space-id'; describe('resourceInstaller', () => { describe("when the common resources don't exist", () => { it('installs the common resources', async () => { const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(false); - const installer = new DefaultResourceInstaller( - mockClusterClient, - loggerMock.create(), - SPACE_ID - ); + const installer = new DefaultResourceInstaller(mockClusterClient, loggerMock.create()); await installer.ensureCommonResourcesInstalled(); @@ -44,7 +39,7 @@ describe('resourceInstaller', () => { expect.objectContaining({ name: SLO_INDEX_TEMPLATE_NAME }) ); expect(mockClusterClient.ingest.putPipeline).toHaveBeenCalledWith( - expect.objectContaining({ id: getSLOIngestPipelineName(SPACE_ID) }) + expect.objectContaining({ id: SLO_INGEST_PIPELINE_NAME }) ); }); }); @@ -55,13 +50,9 @@ describe('resourceInstaller', () => { mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(true); mockClusterClient.ingest.getPipeline.mockResponseOnce({ // @ts-ignore _meta not typed properly - [getSLOIngestPipelineName(SPACE_ID)]: { _meta: { version: SLO_RESOURCES_VERSION } }, + [SLO_INGEST_PIPELINE_NAME]: { _meta: { version: SLO_RESOURCES_VERSION } }, } as IngestGetPipelineResponse); - const installer = new DefaultResourceInstaller( - mockClusterClient, - loggerMock.create(), - SPACE_ID - ); + const installer = new DefaultResourceInstaller(mockClusterClient, loggerMock.create()); await installer.ensureCommonResourcesInstalled(); diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.ts index 8ed8108b3c4b..abc02052097b 100644 --- a/x-pack/plugins/observability/server/services/slo/resource_installer.ts +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.ts @@ -13,7 +13,7 @@ import type { import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { - getSLOIngestPipelineName, + SLO_INGEST_PIPELINE_NAME, SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, SLO_INDEX_TEMPLATE_NAME, @@ -29,11 +29,7 @@ export interface ResourceInstaller { } export class DefaultResourceInstaller implements ResourceInstaller { - constructor( - private esClient: ElasticsearchClient, - private logger: Logger, - private spaceId: string - ) {} + constructor(private esClient: ElasticsearchClient, private logger: Logger) {} public async ensureCommonResourcesInstalled(): Promise { const alreadyInstalled = await this.areResourcesAlreadyInstalled(); @@ -64,8 +60,8 @@ export class DefaultResourceInstaller implements ResourceInstaller { await this.createOrUpdateIngestPipelineTemplate( getSLOPipelineTemplate( - getSLOIngestPipelineName(this.spaceId), - this.getPipelinePrefix(SLO_RESOURCES_VERSION, this.spaceId) + SLO_INGEST_PIPELINE_NAME, + this.getPipelinePrefix(SLO_RESOURCES_VERSION) ) ); } catch (err) { @@ -74,10 +70,10 @@ export class DefaultResourceInstaller implements ResourceInstaller { } } - private getPipelinePrefix(version: number, spaceId: string): string { + private getPipelinePrefix(version: number): string { // Following https://www.elastic.co/blog/an-introduction-to-the-elastic-data-stream-naming-scheme - // slo-observability.sli--. - return `${SLO_INDEX_TEMPLATE_NAME}-v${version}-${spaceId}.`; + // slo-observability.sli-. + return `${SLO_INDEX_TEMPLATE_NAME}-v${version}.`; } private async areResourcesAlreadyInstalled(): Promise { @@ -87,12 +83,11 @@ export class DefaultResourceInstaller implements ResourceInstaller { let ingestPipelineExists = false; try { - const pipelineName = getSLOIngestPipelineName(this.spaceId); - const pipeline = await this.esClient.ingest.getPipeline({ id: pipelineName }); + const pipeline = await this.esClient.ingest.getPipeline({ id: SLO_INGEST_PIPELINE_NAME }); ingestPipelineExists = // @ts-ignore _meta is not defined on the type - pipeline && pipeline[pipelineName]._meta.version === SLO_RESOURCES_VERSION; + pipeline && pipeline[SLO_INGEST_PIPELINE_NAME]._meta.version === SLO_RESOURCES_VERSION; } catch (err) { return false; } diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap index 239c3c93503b..7b7f49061fd5 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap @@ -20,8 +20,8 @@ Object { "version": 1, }, "dest": Object { - "index": "slo-observability.sli-v1-my-namespace", - "pipeline": "slo-observability.sli.monthly-my-namespace", + "index": "slo-observability.sli-v1", + "pipeline": "slo-observability.sli.monthly", }, "frequency": "1m", "pivot": Object { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap index 1fdaec28f897..8b73f76a8082 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap @@ -20,8 +20,8 @@ Object { "version": 1, }, "dest": Object { - "index": "slo-observability.sli-v1-my-namespace", - "pipeline": "slo-observability.sli.monthly-my-namespace", + "index": "slo-observability.sli-v1", + "pipeline": "slo-observability.sli.monthly", }, "frequency": "1m", "pivot": Object { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts index 1e8fadf365d7..ba984e542619 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts @@ -13,7 +13,7 @@ const generator = new ApmTransactionDurationTransformGenerator(); describe('APM Transaction Duration Transform Generator', () => { it('returns the correct transform params with every specified indicator params', async () => { const anSLO = createSLO(createAPMTransactionDurationIndicator()); - const transform = generator.getTransformParams(anSLO, 'my-namespace'); + const transform = generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot({ transform_id: expect.any(String), @@ -34,7 +34,7 @@ describe('APM Transaction Duration Transform Generator', () => { transaction_type: '*', }) ); - const transform = generator.getTransformParams(anSLO, 'my-namespace'); + const transform = generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts index 4c4642173763..bc45e12abbb3 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts @@ -12,8 +12,8 @@ import { } from '@elastic/elasticsearch/lib/api/types'; import { ALL_VALUE } from '../../../types/schema'; import { - getSLODestinationIndexName, - getSLOIngestPipelineName, + SLO_DESTINATION_INDEX_NAME, + SLO_INGEST_PIPELINE_NAME, getSLOTransformId, } from '../../../assets/constants'; import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; @@ -27,7 +27,7 @@ import { TransformGenerator } from '.'; const APM_SOURCE_INDEX = 'metrics-apm*'; export class ApmTransactionDurationTransformGenerator implements TransformGenerator { - public getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest { + public getTransformParams(slo: SLO): TransformPutTransformRequest { if (!apmTransactionDurationSLOSchema.is(slo)) { throw new Error(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -35,7 +35,7 @@ export class ApmTransactionDurationTransformGenerator implements TransformGenera return getSLOTransformTemplate( this.buildTransformId(slo), this.buildSource(slo), - this.buildDestination(slo, spaceId), + this.buildDestination(), this.buildGroupBy(), this.buildAggregations(slo) ); @@ -104,10 +104,10 @@ export class ApmTransactionDurationTransformGenerator implements TransformGenera }; } - private buildDestination(slo: APMTransactionDurationSLO, spaceId: string) { + private buildDestination() { return { - pipeline: getSLOIngestPipelineName(spaceId), - index: getSLODestinationIndexName(spaceId), + pipeline: SLO_INGEST_PIPELINE_NAME, + index: SLO_DESTINATION_INDEX_NAME, }; } diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts index afa904fa1f8c..2bc88c576f8c 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts @@ -13,7 +13,7 @@ const generator = new ApmTransactionErrorRateTransformGenerator(); describe('APM Transaction Error Rate Transform Generator', () => { it('returns the correct transform params with every specified indicator params', async () => { const anSLO = createSLO(createAPMTransactionErrorRateIndicator()); - const transform = generator.getTransformParams(anSLO, 'my-namespace'); + const transform = generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot({ transform_id: expect.any(String), @@ -27,7 +27,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { it("uses default values when 'good_status_codes' is not specified", async () => { const anSLO = createSLO(createAPMTransactionErrorRateIndicator({ good_status_codes: [] })); - const transform = generator.getTransformParams(anSLO, 'my-namespace'); + const transform = generator.getTransformParams(anSLO); expect(transform.pivot?.aggregations).toMatchSnapshot(); }); @@ -41,7 +41,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { transaction_type: '*', }) ); - const transform = generator.getTransformParams(anSLO, 'my-namespace'); + const transform = generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts index 6740bee2b707..23a9a03f6e14 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts @@ -14,8 +14,8 @@ import { ALL_VALUE } from '../../../types/schema'; import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; import { TransformGenerator } from '.'; import { - getSLODestinationIndexName, - getSLOIngestPipelineName, + SLO_DESTINATION_INDEX_NAME, + SLO_INGEST_PIPELINE_NAME, getSLOTransformId, } from '../../../assets/constants'; import { @@ -29,7 +29,7 @@ const ALLOWED_STATUS_CODES = ['2xx', '3xx', '4xx', '5xx']; const DEFAULT_GOOD_STATUS_CODES = ['2xx', '3xx', '4xx']; export class ApmTransactionErrorRateTransformGenerator implements TransformGenerator { - public getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest { + public getTransformParams(slo: SLO): TransformPutTransformRequest { if (!apmTransactionErrorRateSLOSchema.is(slo)) { throw new Error(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -37,7 +37,7 @@ export class ApmTransactionErrorRateTransformGenerator implements TransformGener return getSLOTransformTemplate( this.buildTransformId(slo), this.buildSource(slo), - this.buildDestination(slo, spaceId), + this.buildDestination(), this.buildGroupBy(), this.buildAggregations(slo) ); @@ -106,10 +106,10 @@ export class ApmTransactionErrorRateTransformGenerator implements TransformGener }; } - private buildDestination(slo: APMTransactionErrorRateSLO, spaceId: string) { + private buildDestination() { return { - pipeline: getSLOIngestPipelineName(spaceId), - index: getSLODestinationIndexName(spaceId), + pipeline: SLO_INGEST_PIPELINE_NAME, + index: SLO_DESTINATION_INDEX_NAME, }; } diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts index 21a917ea1af6..3965e809373c 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts @@ -9,5 +9,5 @@ import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typ import { SLO } from '../../../types/models'; export interface TransformGenerator { - getTransformParams(slo: SLO, spaceId: string): TransformPutTransformRequest; + getTransformParams(slo: SLO): TransformPutTransformRequest; } diff --git a/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts b/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts index 1badb6b08e49..434e6841ff0e 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts @@ -23,8 +23,6 @@ import { import { SLO, SLITypes } from '../../types/models'; import { createAPMTransactionErrorRateIndicator, createSLO } from './fixtures/slo'; -const SPACE_ID = 'space-id'; - describe('TransformManager', () => { let esClientMock: ElasticsearchClientMock; let loggerMock: jest.Mocked; @@ -41,7 +39,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_duration': new DummyTransformGenerator(), }; - const service = new DefaultTransformManager(generators, esClientMock, loggerMock, SPACE_ID); + const service = new DefaultTransformManager(generators, esClientMock, loggerMock); await expect( service.install( @@ -63,12 +61,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_duration': new FailTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); await expect( transformManager.install( @@ -92,12 +85,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); const slo = createSLO(createAPMTransactionErrorRateIndicator()); const transformId = await transformManager.install(slo); @@ -113,12 +101,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); await transformManager.start('slo-transform-id'); @@ -132,12 +115,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); await transformManager.stop('slo-transform-id'); @@ -151,12 +129,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); await transformManager.uninstall('slo-transform-id'); @@ -171,12 +144,7 @@ describe('TransformManager', () => { const generators: Record = { 'slo.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), }; - const transformManager = new DefaultTransformManager( - generators, - esClientMock, - loggerMock, - SPACE_ID - ); + const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); await transformManager.uninstall('slo-transform-id'); diff --git a/x-pack/plugins/observability/server/services/slo/transform_manager.ts b/x-pack/plugins/observability/server/services/slo/transform_manager.ts index ab7799a4a00c..154660fccaf9 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_manager.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_manager.ts @@ -24,8 +24,7 @@ export class DefaultTransformManager implements TransformManager { constructor( private generators: Record, private esClient: ElasticsearchClient, - private logger: Logger, - private spaceId: string + private logger: Logger ) {} async install(slo: SLO): Promise { @@ -35,7 +34,7 @@ export class DefaultTransformManager implements TransformManager { throw new Error(`Unsupported SLO type: ${slo.indicator.type}`); } - const transformParams = generator.getTransformParams(slo, this.spaceId); + const transformParams = generator.getTransformParams(slo); try { await retryTransientEsErrors(() => this.esClient.transform.putTransform(transformParams), { logger: this.logger, From 2d6a45b5ef95502e503f17600076680fac5693a0 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau Date: Wed, 28 Sep 2022 08:50:25 -0400 Subject: [PATCH 061/185] [RAM] Add Stats on top of execution logs (#140883) * WIP * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * add new route for global execution KPI + add filter * revert changes * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * update query kpi * add new route for global KPI * Add KPI to log tab and add filtering * Add types * Fix calling API multiple times when refresh, add unit tests * Fix lint and type checks * clean up * remove sorting * add tests * fix global kpi log * fix 141833 * fix query to match with filter * allow access to getRuleExecutionKPI * fix unit test * fix jest tests Co-authored-by: Jiawei Wu Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../alerting/common/execution_log_types.ts | 13 + .../authorization/alerting_authorization.ts | 1 + .../lib/get_execution_log_aggregation.test.ts | 583 ++++++++++++++++++ .../lib/get_execution_log_aggregation.ts | 199 +++++- .../routes/get_global_execution_kpi.test.ts | 69 +++ .../server/routes/get_global_execution_kpi.ts | 50 ++ .../routes/get_rule_execution_kpi.test.ts | 102 +++ .../server/routes/get_rule_execution_kpi.ts | 56 ++ .../plugins/alerting/server/routes/index.ts | 4 + .../alerting/server/rules_client.mock.ts | 2 + .../server/rules_client/audit_events.ts | 14 + .../server/rules_client/rules_client.ts | 134 ++++ .../server/task_runner/task_runner.ts | 1 - .../alerting.test.ts | 8 + .../feature_privilege_builder/alerting.ts | 9 +- .../application/lib/rule_api/get_filter.ts | 34 + .../public/application/lib/rule_api/index.ts | 4 + .../rule_api/load_action_error_log.test.ts | 2 +- .../lib/rule_api/load_action_error_log.ts | 17 +- .../load_execution_kpi_aggregations.ts | 41 ++ .../load_execution_log_aggregations.ts | 17 +- .../load_global_execution_kpi_aggregations.ts | 38 ++ .../with_bulk_rule_api_operations.tsx | 32 +- .../rule_details/components/rule.test.tsx | 4 +- .../sections/rule_details/components/rule.tsx | 16 +- .../rule_event_log_list_kpi.test.tsx | 183 ++++++ .../components/rule_event_log_list_kpi.tsx | 251 ++++++++ .../components/rule_event_log_list_table.tsx | 19 +- .../alerting/get_global_execution_kpi.ts | 162 +++++ .../tests/alerting/get_rule_execution_kpi.ts | 133 ++++ 30 files changed, 2143 insertions(+), 55 deletions(-) create mode 100644 x-pack/plugins/alerting/server/routes/get_global_execution_kpi.test.ts create mode 100644 x-pack/plugins/alerting/server/routes/get_global_execution_kpi.ts create mode 100644 x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts create mode 100644 x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/get_filter.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_kpi_aggregations.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_global_execution_kpi_aggregations.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.tsx create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/get_global_execution_kpi.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/get_rule_execution_kpi.ts diff --git a/x-pack/plugins/alerting/common/execution_log_types.ts b/x-pack/plugins/alerting/common/execution_log_types.ts index 1938d8be4acd..b08a0bf76d69 100644 --- a/x-pack/plugins/alerting/common/execution_log_types.ts +++ b/x-pack/plugins/alerting/common/execution_log_types.ts @@ -26,6 +26,17 @@ export const actionErrorLogSortableColumns = [ 'event.action', ]; +export const EMPTY_EXECUTION_KPI_RESULT = { + success: 0, + unknown: 0, + failure: 0, + activeAlerts: 0, + newAlerts: 0, + recoveredAlerts: 0, + erroredActions: 0, + triggeredActions: 0, +}; + export type ExecutionLogSortFields = typeof executionLogSortableColumns[number]; export type ActionErrorLogSortFields = typeof actionErrorLogSortableColumns[number]; @@ -68,3 +79,5 @@ export interface IExecutionLogResult { total: number; data: IExecutionLog[]; } + +export type IExecutionKPIResult = typeof EMPTY_EXECUTION_KPI_RESULT; diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts index 0c501f25a857..7138aabe9d26 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts @@ -35,6 +35,7 @@ export enum ReadOperations { Find = 'find', GetAuthorizedAlertsIndices = 'getAuthorizedAlertsIndices', RunSoon = 'runSoon', + GetRuleExecutionKPI = 'getRuleExecutionKPI', } export enum WriteOperations { diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index e48483785490..eb46ae67e2ef 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -13,6 +13,8 @@ import { formatSortForBucketSort, formatSortForTermSort, ExecutionUuidAggResult, + getExecutionKPIAggregation, + formatExecutionKPIResult, } from './get_execution_log_aggregation'; describe('formatSortForBucketSort', () => { @@ -1689,3 +1691,584 @@ describe('formatExecutionLogResult', () => { }); }); }); + +describe('getExecutionKPIAggregation', () => { + test('should correctly generate aggregation', () => { + expect(getExecutionKPIAggregation()).toEqual({ + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuid: { + terms: { + field: 'kibana.alert.rule.execution.uuid', + size: 1000, + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + from: 0, + size: 1000, + gap_policy: 'insert_zeros', + }, + }, + actionExecution: { + filter: { + bool: { + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'actions', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + actionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + ruleExecution: { + filter: { + bool: { + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + numTriggeredActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + missing: 0, + }, + }, + numGeneratedActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + missing: 0, + }, + }, + numActiveAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + missing: 0, + }, + }, + numRecoveredAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + missing: 0, + }, + }, + numNewAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + missing: 0, + }, + }, + ruleExecutionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + }, + }, + }, + }, + }); + }); + + test('should correctly generate aggregation with a defined filter in the form of a string', () => { + expect(getExecutionKPIAggregation('test:test')).toEqual({ + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuid: { + terms: { + field: 'kibana.alert.rule.execution.uuid', + size: 1000, + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + from: 0, + size: 1000, + gap_policy: 'insert_zeros', + }, + }, + actionExecution: { + filter: { + bool: { + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'actions', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + actionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + ruleExecution: { + filter: { + bool: { + filter: { + bool: { + should: [ + { + match: { + test: 'test', + }, + }, + ], + minimum_should_match: 1, + }, + }, + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + numTriggeredActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + missing: 0, + }, + }, + numGeneratedActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + missing: 0, + }, + }, + numActiveAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + missing: 0, + }, + }, + numRecoveredAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + missing: 0, + }, + }, + numNewAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + missing: 0, + }, + }, + ruleExecutionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + }, + }, + }, + }, + }); + }); + + test('should correctly generate aggregation with a defined filter in the form of a KueryNode', () => { + expect(getExecutionKPIAggregation(fromKueryExpression('test:test'))).toEqual({ + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuid: { + terms: { + field: 'kibana.alert.rule.execution.uuid', + size: 1000, + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + from: 0, + size: 1000, + gap_policy: 'insert_zeros', + }, + }, + actionExecution: { + filter: { + bool: { + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'actions', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + actionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + ruleExecution: { + filter: { + bool: { + filter: { + bool: { + should: [ + { + match: { + test: 'test', + }, + }, + ], + minimum_should_match: 1, + }, + }, + must: [ + { + bool: { + must: [ + { + match: { + 'event.action': 'execute', + }, + }, + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + numTriggeredActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + missing: 0, + }, + }, + numGeneratedActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + missing: 0, + }, + }, + numActiveAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + missing: 0, + }, + }, + numRecoveredAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + missing: 0, + }, + }, + numNewAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + missing: 0, + }, + }, + ruleExecutionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + }, + }, + }, + }, + }); + }); +}); + +describe('formatExecutionKPIAggBuckets', () => { + test('should return empty results if aggregations are undefined', () => { + expect( + formatExecutionKPIResult({ + aggregations: undefined, + }) + ).toEqual({ + activeAlerts: 0, + erroredActions: 0, + failure: 0, + newAlerts: 0, + recoveredAlerts: 0, + success: 0, + triggeredActions: 0, + unknown: 0, + }); + }); + + test('should format results correctly', () => { + const results = { + aggregations: { + excludeExecuteStart: { + meta: {}, + doc_count: 875, + executionUuid: { + meta: {}, + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + ruleExecution: { + meta: {}, + doc_count: 3, + numTriggeredActions: { + value: 5.0, + }, + numGeneratedActions: { + value: 5.0, + }, + numActiveAlerts: { + value: 5.0, + }, + numNewAlerts: { + value: 5.0, + }, + numRecoveredAlerts: { + value: 0.0, + }, + ruleExecutionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'success', + doc_count: 3, + }, + ], + }, + }, + actionExecution: { + meta: {}, + doc_count: 5, + actionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'success', + doc_count: 5, + }, + ], + }, + }, + }, + { + ruleExecution: { + meta: {}, + doc_count: 2, + numTriggeredActions: { + value: 5.0, + }, + numGeneratedActions: { + value: 5.0, + }, + numActiveAlerts: { + value: 5.0, + }, + numNewAlerts: { + value: 5.0, + }, + numRecoveredAlerts: { + value: 0.0, + }, + ruleExecutionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'success', + doc_count: 2, + }, + ], + }, + }, + actionExecution: { + meta: {}, + doc_count: 3, + actionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'failure', + doc_count: 3, + }, + ], + }, + }, + }, + ], + }, + }, + }, + }; + + expect(formatExecutionKPIResult(results)).toEqual({ + success: 5, + unknown: 0, + failure: 0, + activeAlerts: 10, + newAlerts: 10, + recoveredAlerts: 0, + erroredActions: 3, + triggeredActions: 10, + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index 0854488d5f29..fedc827a46c7 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -12,7 +12,7 @@ import { flatMap, get } from 'lodash'; import { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { parseDuration } from '.'; -import { IExecutionLog, IExecutionLogResult } from '../../common'; +import { IExecutionLog, IExecutionLogResult, EMPTY_EXECUTION_KPI_RESULT } from '../../common'; const DEFAULT_MAX_BUCKETS_LIMIT = 1000; // do not retrieve more than this number of executions @@ -51,6 +51,21 @@ interface IActionExecution buckets: Array<{ key: string; doc_count: number }>; } +interface IExecutionUuidKpiAggBucket extends estypes.AggregationsStringTermsBucketKeys { + actionExecution: { + doc_count: number; + actionOutcomes: IActionExecution; + }; + ruleExecution: { + doc_count: number; + numTriggeredActions: estypes.AggregationsSumAggregate; + numGeneratedActions: estypes.AggregationsSumAggregate; + numActiveAlerts: estypes.AggregationsSumAggregate; + numRecoveredAlerts: estypes.AggregationsSumAggregate; + numNewAlerts: estypes.AggregationsSumAggregate; + ruleExecutionOutcomes: IActionExecution; + }; +} interface IExecutionUuidAggBucket extends estypes.AggregationsStringTermsBucketKeys { timeoutMessage: estypes.AggregationsMultiBucketBase; ruleExecution: { @@ -76,12 +91,22 @@ export interface ExecutionUuidAggResult buckets: TBucket[]; } +export interface ExecutionUuidKPIAggResult + extends estypes.AggregationsAggregateBase { + buckets: TBucket[]; +} + interface ExcludeExecuteStartAggResult extends estypes.AggregationsAggregateBase { executionUuid: ExecutionUuidAggResult; executionUuidCardinality: { executionUuidCardinality: estypes.AggregationsCardinalityAggregate; }; } + +interface ExcludeExecuteStartKpiAggResult extends estypes.AggregationsAggregateBase { + executionUuid: ExecutionUuidKPIAggResult; +} + export interface IExecutionLogAggOptions { filter?: string | KueryNode; page: number; @@ -102,6 +127,115 @@ const ExecutionLogSortFields: Record = { num_new_alerts: 'ruleExecution>numNewAlerts', }; +export const getExecutionKPIAggregation = (filter?: IExecutionLogAggOptions['filter']) => { + const dslFilterQuery: estypes.QueryDslBoolQuery['filter'] = buildDslFilterQuery(filter); + + return { + excludeExecuteStart: { + filter: { + bool: { + must_not: [ + { + term: { + 'event.action': 'execute-start', + }, + }, + ], + }, + }, + aggs: { + executionUuid: { + // Bucket by execution UUID + terms: { + field: EXECUTION_UUID_FIELD, + size: DEFAULT_MAX_BUCKETS_LIMIT, + }, + aggs: { + executionUuidSorted: { + bucket_sort: { + from: 0, + size: 1000, + gap_policy: 'insert_zeros' as estypes.AggregationsGapPolicy, + }, + }, + actionExecution: { + filter: { + bool: { + must: [getProviderAndActionFilter('actions', 'execute')], + }, + }, + aggs: { + actionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + ruleExecution: { + filter: { + bool: { + ...(dslFilterQuery ? { filter: dslFilterQuery } : {}), + must: [getProviderAndActionFilter('alerting', 'execute')], + }, + }, + aggs: { + numTriggeredActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', + missing: 0, + }, + }, + numGeneratedActions: { + sum: { + field: 'kibana.alert.rule.execution.metrics.number_of_generated_actions', + missing: 0, + }, + }, + numActiveAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.active', + missing: 0, + }, + }, + numRecoveredAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.recovered', + missing: 0, + }, + }, + numNewAlerts: { + sum: { + field: 'kibana.alert.rule.execution.metrics.alert_counts.new', + missing: 0, + }, + }, + ruleExecutionOutcomes: { + terms: { + field: 'event.outcome', + size: 2, + }, + }, + }, + }, + minExecutionUuidBucket: { + bucket_selector: { + buckets_path: { + count: 'ruleExecution._count', + }, + script: { + source: 'params.count > 0', + }, + }, + }, + }, + }, + }, + }, + }; +}; + export function getExecutionLogAggregation({ filter, page, @@ -130,13 +264,7 @@ export function getExecutionLogAggregation({ throw Boom.badRequest(`Invalid perPage field "${perPage}" - must be greater than 0`); } - let dslFilterQuery: estypes.QueryDslBoolQuery['filter']; - try { - const filterKueryNode = typeof filter === 'string' ? fromKueryExpression(filter) : filter; - dslFilterQuery = filter ? toElasticsearchQuery(filterKueryNode) : undefined; - } catch (err) { - throw Boom.badRequest(`Invalid kuery syntax for filter ${filter}`); - } + const dslFilterQuery: estypes.QueryDslBoolQuery['filter'] = buildDslFilterQuery(filter); return { excludeExecuteStart: { @@ -295,6 +423,15 @@ export function getExecutionLogAggregation({ }; } +function buildDslFilterQuery(filter: IExecutionLogAggOptions['filter']) { + try { + const filterKueryNode = typeof filter === 'string' ? fromKueryExpression(filter) : filter; + return filter ? toElasticsearchQuery(filterKueryNode) : undefined; + } catch (err) { + throw Boom.badRequest(`Invalid kuery syntax for filter ${filter}`); + } +} + function getProviderAndActionFilter(provider: string, action: string) { return { bool: { @@ -362,6 +499,52 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio }; } +function formatExecutionKPIAggBuckets(buckets: IExecutionUuidKpiAggBucket[]) { + const objToReturn = { + success: 0, + unknown: 0, + failure: 0, + activeAlerts: 0, + newAlerts: 0, + recoveredAlerts: 0, + erroredActions: 0, + triggeredActions: 0, + }; + + buckets.forEach((bucket) => { + const ruleExecutionOutcomes = bucket?.ruleExecution?.ruleExecutionOutcomes?.buckets ?? []; + const actionExecutionOutcomes = bucket?.actionExecution?.actionOutcomes?.buckets ?? []; + + const ruleExecutionCount = bucket?.ruleExecution?.doc_count ?? 0; + const successRuleExecution = + ruleExecutionOutcomes.find((subBucket) => subBucket?.key === 'success')?.doc_count ?? 0; + const failureRuleExecution = + ruleExecutionOutcomes.find((subBucket) => subBucket?.key === 'failure')?.doc_count ?? 0; + + objToReturn.success += successRuleExecution; + objToReturn.unknown += ruleExecutionCount - (successRuleExecution + failureRuleExecution); + objToReturn.failure += failureRuleExecution; + objToReturn.activeAlerts += bucket?.ruleExecution?.numActiveAlerts.value ?? 0; + objToReturn.newAlerts += bucket?.ruleExecution?.numNewAlerts.value ?? 0; + objToReturn.recoveredAlerts += bucket?.ruleExecution?.numRecoveredAlerts.value ?? 0; + objToReturn.erroredActions += + actionExecutionOutcomes.find((subBucket) => subBucket?.key === 'failure')?.doc_count ?? 0; + objToReturn.triggeredActions += bucket?.ruleExecution?.numTriggeredActions.value ?? 0; + }); + + return objToReturn; +} + +export function formatExecutionKPIResult(results: AggregateEventsBySavedObjectResult) { + const { aggregations } = results; + if (!aggregations || !aggregations.excludeExecuteStart) { + return EMPTY_EXECUTION_KPI_RESULT; + } + const aggs = aggregations.excludeExecuteStart as ExcludeExecuteStartKpiAggResult; + const buckets = aggs.executionUuid.buckets; + return formatExecutionKPIAggBuckets(buckets); +} + export function formatExecutionLogResult( results: AggregateEventsBySavedObjectResult ): IExecutionLogResult { diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.test.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.test.ts new file mode 100644 index 000000000000..89b21547c892 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { rulesClientMock } from '../rules_client.mock'; +import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; + +const rulesClient = rulesClientMock.create(); +jest.mock('../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getGlobalExecutionKPIRoute', () => { + const dateString = new Date().toISOString(); + const mockedExecutionLog = { + success: 3, + unknown: 0, + failure: 0, + activeAlerts: 5, + newAlerts: 5, + recoveredAlerts: 0, + erroredActions: 0, + triggeredActions: 5, + }; + + it('gets global execution KPI', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getGlobalExecutionKPIRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/_global_execution_kpi"`); + + rulesClient.getGlobalExecutionKpiWithAuth.mockResolvedValue(mockedExecutionLog); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + date_start: dateString, + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(rulesClient.getGlobalExecutionKpiWithAuth).toHaveBeenCalledTimes(1); + expect(rulesClient.getGlobalExecutionKpiWithAuth.mock.calls[0]).toEqual([ + { + dateStart: dateString, + }, + ]); + + expect(res.ok).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.ts new file mode 100644 index 000000000000..29937cc3d8c9 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_kpi.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { GetGlobalExecutionKPIParams } from '../rules_client'; +import { ILicenseState } from '../lib'; + +const querySchema = schema.object({ + date_start: schema.string(), + date_end: schema.maybe(schema.string()), + filter: schema.maybe(schema.string()), +}); + +const rewriteReq: RewriteRequestCase = ({ + date_start: dateStart, + date_end: dateEnd, + ...rest +}) => ({ + ...rest, + dateStart, + dateEnd, +}); + +export const getGlobalExecutionKPIRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/_global_execution_kpi`, + validate: { + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const rulesClient = (await context.alerting).getRulesClient(); + return res.ok({ + body: await rulesClient.getGlobalExecutionKpiWithAuth(rewriteReq(req.query)), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts new file mode 100644 index 000000000000..db5033404788 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { mockHandlerArguments } from './_mock_handler_arguments'; +import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import { rulesClientMock } from '../rules_client.mock'; +import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi'; + +const rulesClient = rulesClientMock.create(); +jest.mock('../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getRuleExecutionKPIRoute', () => { + const dateString = new Date().toISOString(); + const mockedExecutionLog = { + success: 3, + unknown: 0, + failure: 0, + activeAlerts: 5, + newAlerts: 5, + recoveredAlerts: 0, + erroredActions: 0, + triggeredActions: 5, + }; + + it('gets rule execution KPI', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleExecutionKPIRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/_execution_kpi"`); + + rulesClient.getRuleExecutionKPI.mockResolvedValue(mockedExecutionLog); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { + id: '1', + }, + query: { + date_start: dateString, + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(rulesClient.getRuleExecutionKPI).toHaveBeenCalledTimes(1); + expect(rulesClient.getRuleExecutionKPI.mock.calls[0]).toEqual([ + { + dateStart: dateString, + id: '1', + }, + ]); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('returns NOT-FOUND when rule is not found', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleExecutionKPIRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + rulesClient.getRuleExecutionKPI = jest + .fn() + .mockRejectedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { + id: '1', + }, + query: {}, + }, + ['notFound'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot( + `[Error: Saved object [alert/1] not found]` + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.ts new file mode 100644 index 000000000000..11f7085c5329 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { GetRuleExecutionKPIParams } from '../rules_client'; +import { ILicenseState } from '../lib'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const querySchema = schema.object({ + date_start: schema.string(), + date_end: schema.maybe(schema.string()), + filter: schema.maybe(schema.string()), +}); + +const rewriteReq: RewriteRequestCase = ({ + date_start: dateStart, + date_end: dateEnd, + ...rest +}) => ({ + ...rest, + dateStart, + dateEnd, +}); + +export const getRuleExecutionKPIRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rule/{id}/_execution_kpi`, + validate: { + params: paramSchema, + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const rulesClient = (await context.alerting).getRulesClient(); + const { id } = req.params; + return res.ok({ + body: await rulesClient.getRuleExecutionKPI(rewriteReq({ id, ...req.query })), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index dca2e214d078..de9e2f112d9e 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -22,7 +22,9 @@ import { findRulesRoute, findInternalRulesRoute } from './find_rules'; import { getRuleAlertSummaryRoute } from './get_rule_alert_summary'; import { getRuleExecutionLogRoute } from './get_rule_execution_log'; import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; +import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; import { getActionErrorLogRoute } from './get_action_error_log'; +import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi'; import { getRuleStateRoute } from './get_rule_state'; import { healthRoute } from './health'; import { resolveRuleRoute } from './resolve_rule'; @@ -63,6 +65,8 @@ export function defineRoutes(opts: RouteOptions) { getRuleExecutionLogRoute(router, licenseState); getGlobalExecutionLogRoute(router, licenseState); getActionErrorLogRoute(router, licenseState); + getRuleExecutionKPIRoute(router, licenseState); + getGlobalExecutionKPIRoute(router, licenseState); getRuleStateRoute(router, licenseState); healthRoute(router, licenseState, encryptedSavedObjects); ruleTypesRoute(router, licenseState); diff --git a/x-pack/plugins/alerting/server/rules_client.mock.ts b/x-pack/plugins/alerting/server/rules_client.mock.ts index 7333ad59bbb7..2092b98e48c0 100644 --- a/x-pack/plugins/alerting/server/rules_client.mock.ts +++ b/x-pack/plugins/alerting/server/rules_client.mock.ts @@ -30,6 +30,8 @@ const createRulesClientMock = () => { listAlertTypes: jest.fn(), getAlertSummary: jest.fn(), getExecutionLogForRule: jest.fn(), + getRuleExecutionKPI: jest.fn(), + getGlobalExecutionKpiWithAuth: jest.fn(), getGlobalExecutionLogWithAuth: jest.fn(), getActionErrorLog: jest.fn(), getSpaceId: jest.fn(), diff --git a/x-pack/plugins/alerting/server/rules_client/audit_events.ts b/x-pack/plugins/alerting/server/rules_client/audit_events.ts index c29bc99aa6ed..30b759c895c4 100644 --- a/x-pack/plugins/alerting/server/rules_client/audit_events.ts +++ b/x-pack/plugins/alerting/server/rules_client/audit_events.ts @@ -26,7 +26,9 @@ export enum RuleAuditAction { BULK_EDIT = 'rule_bulk_edit', GET_EXECUTION_LOG = 'rule_get_execution_log', GET_GLOBAL_EXECUTION_LOG = 'rule_get_global_execution_log', + GET_GLOBAL_EXECUTION_KPI = 'rule_get_global_execution_kpi', GET_ACTION_ERROR_LOG = 'rule_get_action_error_log', + GET_RULE_EXECUTION_KPI = 'rule_get_execution_kpi', SNOOZE = 'rule_snooze', UNSNOOZE = 'rule_unsnooze', RUN_SOON = 'rule_run_soon', @@ -68,6 +70,16 @@ const eventVerbs: Record = { rule_snooze: ['snooze', 'snoozing', 'snoozed'], rule_unsnooze: ['unsnooze', 'unsnoozing', 'unsnoozed'], rule_run_soon: ['run', 'running', 'ran'], + rule_get_execution_kpi: [ + 'access execution KPI for', + 'accessing execution KPI for', + 'accessed execution KPI for', + ], + rule_get_global_execution_kpi: [ + 'access global execution KPI for', + 'accessing global execution KPI for', + 'accessed global execution KPI for', + ], }; const eventTypes: Record = { @@ -92,6 +104,8 @@ const eventTypes: Record = { rule_snooze: 'change', rule_unsnooze: 'change', rule_run_soon: 'access', + rule_get_execution_kpi: 'access', + rule_get_global_execution_kpi: 'access', }; export interface RuleAuditEventParams { diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index c2a643ce8c29..f865a0472465 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -118,7 +118,9 @@ import { import { AlertingRulesConfig } from '../config'; import { formatExecutionLogResult, + formatExecutionKPIResult, getExecutionLogAggregation, + getExecutionKPIAggregation, } from '../lib/get_execution_log_aggregation'; import { IExecutionLogResult, IExecutionErrorsResult } from '../../common'; import { validateSnoozeStartDate } from '../lib/validate_snooze_date'; @@ -381,6 +383,19 @@ export interface GetExecutionLogByIdParams { sort: estypes.Sort; } +export interface GetRuleExecutionKPIParams { + id: string; + dateStart: string; + dateEnd?: string; + filter?: string; +} + +export interface GetGlobalExecutionKPIParams { + dateStart: string; + dateEnd?: string; + filter?: string; +} + export interface GetGlobalExecutionLogParams { dateStart: string; dateEnd?: string; @@ -1039,6 +1054,125 @@ export class RulesClient { } } + public async getGlobalExecutionKpiWithAuth({ + dateStart, + dateEnd, + filter, + }: GetGlobalExecutionKPIParams) { + this.logger.debug(`getGlobalExecutionLogWithAuth(): getting global execution log`); + + let authorizationTuple; + try { + authorizationTuple = await this.authorization.getFindAuthorizationFilter( + AlertingAuthorizationEntity.Alert, + { + type: AlertingAuthorizationFilterType.KQL, + fieldNames: { + ruleTypeId: 'kibana.alert.rule.rule_type_id', + consumer: 'kibana.alert.rule.consumer', + }, + } + ); + } catch (error) { + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_GLOBAL_EXECUTION_KPI, + error, + }) + ); + throw error; + } + + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_GLOBAL_EXECUTION_KPI, + }) + ); + + const dateNow = new Date(); + const parsedDateStart = parseDate(dateStart, 'dateStart', dateNow); + const parsedDateEnd = parseDate(dateEnd, 'dateEnd', dateNow); + + const eventLogClient = await this.getEventLogClient(); + + try { + const aggResult = await eventLogClient.aggregateEventsWithAuthFilter( + 'alert', + authorizationTuple.filter as KueryNode, + { + start: parsedDateStart.toISOString(), + end: parsedDateEnd.toISOString(), + aggs: getExecutionKPIAggregation(filter), + } + ); + + return formatExecutionKPIResult(aggResult); + } catch (err) { + this.logger.debug( + `rulesClient.getGlobalExecutionKpiWithAuth(): error searching global execution KPI: ${err.message}` + ); + throw err; + } + } + + public async getRuleExecutionKPI({ id, dateStart, dateEnd, filter }: GetRuleExecutionKPIParams) { + this.logger.debug(`getRuleExecutionKPI(): getting execution KPI for rule ${id}`); + const rule = (await this.get({ id, includeLegacyId: true })) as SanitizedRuleWithLegacyId; + + try { + // Make sure user has access to this rule + await this.authorization.ensureAuthorized({ + ruleTypeId: rule.alertTypeId, + consumer: rule.consumer, + operation: ReadOperations.GetRuleExecutionKPI, + entity: AlertingAuthorizationEntity.Rule, + }); + } catch (error) { + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_RULE_EXECUTION_KPI, + savedObject: { type: 'alert', id }, + error, + }) + ); + throw error; + } + + this.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.GET_RULE_EXECUTION_KPI, + savedObject: { type: 'alert', id }, + }) + ); + + // default duration of instance summary is 60 * rule interval + const dateNow = new Date(); + const parsedDateStart = parseDate(dateStart, 'dateStart', dateNow); + const parsedDateEnd = parseDate(dateEnd, 'dateEnd', dateNow); + + const eventLogClient = await this.getEventLogClient(); + + try { + const aggResult = await eventLogClient.aggregateEventsBySavedObjectIds( + 'alert', + [id], + { + start: parsedDateStart.toISOString(), + end: parsedDateEnd.toISOString(), + aggs: getExecutionKPIAggregation(filter), + }, + rule.legacyId !== null ? [rule.legacyId] : undefined + ); + + return formatExecutionKPIResult(aggResult); + } catch (err) { + this.logger.debug( + `rulesClient.getRuleExecutionKPI(): error searching execution KPI for rule ${id}: ${err.message}` + ); + throw err; + } + } + public async find({ options: { fields, ...options } = {}, excludeFromPublicApi = false, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 457b2872faa6..41029af56752 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -420,7 +420,6 @@ export class TaskRunner< checkHasReachedAlertLimit(); this.alertingEventLogger.setExecutionSucceeded(`rule executed: ${ruleLabel}`); - ruleRunMetricsStore.setSearchMetrics([ wrappedScopedClusterClient.getMetrics(), wrappedSearchSourceClient.getMetrics(), diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts index 8272c9220e10..cd18a28e0d37 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts @@ -90,6 +90,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", ] `); }); @@ -174,6 +175,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/get", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/find", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/getAuthorizedAlertsIndices", @@ -218,6 +220,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -316,6 +319,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -374,6 +378,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -392,6 +397,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getRuleExecutionKPI", ] `); }); @@ -480,6 +486,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -498,6 +505,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getAlertSummary", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/find", + "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getRuleExecutionKPI", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/get", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/find", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/getAuthorizedAlertsIndices", diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts index 542dfd1267d4..a11a4fa77bcd 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts @@ -17,7 +17,14 @@ enum AlertingEntity { } const readOperations: Record = { - rule: ['get', 'getRuleState', 'getAlertSummary', 'getExecutionLog', 'find'], + rule: [ + 'get', + 'getRuleState', + 'getAlertSummary', + 'getExecutionLog', + 'find', + 'getRuleExecutionKPI', + ], alert: ['get', 'find', 'getAuthorizedAlertsIndices'], }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/get_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/get_filter.ts new file mode 100644 index 000000000000..65ace4fa72c7 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/get_filter.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// TODO (Jiawei): Use node builder instead of strings +export const getFilter = ({ + message, + outcomeFilter, + runId, +}: { + message?: string; + outcomeFilter?: string[]; + runId?: string; +}) => { + const filter: string[] = []; + + if (message) { + const escapedMessage = message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&'); + filter.push(`message: "${escapedMessage}" OR error.message: "${escapedMessage}"`); + } + + if (outcomeFilter && outcomeFilter.length) { + filter.push(`event.outcome: ${outcomeFilter.join(' or ')}`); + } + + if (runId) { + filter.push(`kibana.alert.rule.execution.uuid: ${runId}`); + } + + return filter; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts index e841506595c0..e23fe787e87c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts @@ -28,6 +28,10 @@ export { loadExecutionLogAggregations, loadGlobalExecutionLogAggregations, } from './load_execution_log_aggregations'; +export type { LoadExecutionKPIAggregationsProps } from './load_execution_kpi_aggregations'; +export { loadExecutionKPIAggregations } from './load_execution_kpi_aggregations'; +export type { LoadGlobalExecutionKPIAggregationsProps } from './load_global_execution_kpi_aggregations'; +export { loadGlobalExecutionKPIAggregations } from './load_global_execution_kpi_aggregations'; export type { LoadActionErrorLogProps } from './load_action_error_log'; export { loadActionErrorLog } from './load_action_error_log'; export { unmuteAlertInstance } from './unmute_alert'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.test.ts index b4384713336f..d06447be31fb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.test.ts @@ -117,7 +117,7 @@ describe('loadActionErrorLog', () => { "query": Object { "date_end": "2022-03-23T16:17:53.482Z", "date_start": "2022-03-23T16:17:53.482Z", - "filter": "kibana.alert.rule.execution.uuid: 123 and message: \\"test\\" OR error.message: \\"test\\"", + "filter": "message: \\"test\\" OR error.message: \\"test\\" and kibana.alert.rule.execution.uuid: 123", "page": 1, "per_page": 10, "sort": "[{\\"@timestamp\\":{\\"order\\":\\"asc\\"}}]", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.ts index 4ec1a6949bfe..10f2879085cd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_action_error_log.ts @@ -9,6 +9,7 @@ import { HttpSetup } from '@kbn/core/public'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IExecutionErrorsResult, ActionErrorLogSortFields } from '@kbn/alerting-plugin/common'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; +import { getFilter } from './get_filter'; export type SortField = Record< ActionErrorLogSortFields, @@ -49,22 +50,6 @@ const getRenamedSort = (sort?: SortField[]) => { }); }; -// TODO (Jiawei): Use node builder instead of strings -const getFilter = ({ runId, message }: { runId?: string; message?: string }) => { - const filter: string[] = []; - - if (runId) { - filter.push(`kibana.alert.rule.execution.uuid: ${runId}`); - } - - if (message) { - const escapedMessage = message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&'); - filter.push(`message: "${escapedMessage}" OR error.message: "${escapedMessage}"`); - } - - return filter; -}; - export const loadActionErrorLog = ({ id, http, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_kpi_aggregations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_kpi_aggregations.ts new file mode 100644 index 000000000000..076e1167f444 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_kpi_aggregations.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpSetup } from '@kbn/core/public'; +import { IExecutionKPIResult } from '@kbn/alerting-plugin/common'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; +import { getFilter } from './get_filter'; + +export interface LoadExecutionKPIAggregationsProps { + id: string; + outcomeFilter?: string[]; + message?: string; + dateStart: string; + dateEnd?: string; +} + +export const loadExecutionKPIAggregations = ({ + id, + http, + outcomeFilter, + message, + dateStart, + dateEnd, +}: LoadExecutionKPIAggregationsProps & { http: HttpSetup }) => { + const filter = getFilter({ outcomeFilter, message }); + + return http.get( + `${INTERNAL_BASE_ALERTING_API_PATH}/rule/${id}/_execution_kpi`, + { + query: { + filter: filter.length ? filter.join(' and ') : undefined, + date_start: dateStart, + date_end: dateEnd, + }, + } + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_log_aggregations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_log_aggregations.ts index c1f8487d842c..bf5e529499b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_log_aggregations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_execution_log_aggregations.ts @@ -16,6 +16,7 @@ import { } from '@kbn/alerting-plugin/common'; import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; +import { getFilter } from './get_filter'; const getRenamedLog = (data: IExecutionLog) => { const { @@ -40,22 +41,6 @@ const rewriteBodyRes: RewriteRequestCase = ({ data, ...rest ...rest, }); -// TODO (Jiawei): Use node builder instead of strings -const getFilter = ({ outcomeFilter, message }: { outcomeFilter?: string[]; message?: string }) => { - const filter: string[] = []; - - if (outcomeFilter && outcomeFilter.length) { - filter.push(`event.outcome: ${outcomeFilter.join(' or ')}`); - } - - if (message) { - const escapedMessage = message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&'); - filter.push(`message: "${escapedMessage}" OR error.message: "${escapedMessage}"`); - } - - return filter; -}; - export type SortField = Record< ExecutionLogSortFields, { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_global_execution_kpi_aggregations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_global_execution_kpi_aggregations.ts new file mode 100644 index 000000000000..332e14ad4383 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/load_global_execution_kpi_aggregations.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpSetup } from '@kbn/core/public'; +import { IExecutionKPIResult } from '@kbn/alerting-plugin/common'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; +import { getFilter } from './get_filter'; + +export interface LoadGlobalExecutionKPIAggregationsProps { + id: string; + outcomeFilter?: string[]; + message?: string; + dateStart: string; + dateEnd?: string; +} + +export const loadGlobalExecutionKPIAggregations = ({ + id, + http, + outcomeFilter, + message, + dateStart, + dateEnd, +}: LoadGlobalExecutionKPIAggregationsProps & { http: HttpSetup }) => { + const filter = getFilter({ outcomeFilter, message }); + + return http.get(`${INTERNAL_BASE_ALERTING_API_PATH}/_global_execution_kpi`, { + query: { + filter: filter.length ? filter.join(' and ') : undefined, + date_start: dateStart, + date_end: dateEnd, + }, + }); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx index 3150a4cdf407..fa93ae18ec70 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx @@ -7,7 +7,11 @@ import React from 'react'; -import { IExecutionLogResult, IExecutionErrorsResult } from '@kbn/alerting-plugin/common'; +import { + IExecutionLogResult, + IExecutionErrorsResult, + IExecutionKPIResult, +} from '@kbn/alerting-plugin/common'; import { Rule, RuleType, @@ -46,6 +50,10 @@ import { bulkSnoozeRules, BulkSnoozeRulesProps, unsnoozeRule, + loadExecutionKPIAggregations, + LoadExecutionKPIAggregationsProps, + loadGlobalExecutionKPIAggregations, + LoadGlobalExecutionKPIAggregationsProps, bulkUnsnoozeRules, BulkUnsnoozeRulesProps, } from '../../../lib/rule_api'; @@ -74,12 +82,18 @@ export interface ComponentOpts { loadRuleState: (id: Rule['id']) => Promise; loadRuleSummary: (id: Rule['id'], numberOfExecutions?: number) => Promise; loadRuleTypes: () => Promise; + loadExecutionKPIAggregations: ( + props: LoadExecutionKPIAggregationsProps + ) => Promise; loadExecutionLogAggregations: ( props: LoadExecutionLogAggregationsProps ) => Promise; loadGlobalExecutionLogAggregations: ( props: LoadGlobalExecutionLogAggregationsProps ) => Promise; + loadGlobalExecutionKPIAggregations: ( + props: LoadGlobalExecutionKPIAggregationsProps + ) => Promise; loadActionErrorLog: (props: LoadActionErrorLogProps) => Promise; getHealth: () => Promise; resolveRule: (id: Rule['id']) => Promise; @@ -177,6 +191,22 @@ export function withBulkRuleOperations( http, }) } + loadExecutionKPIAggregations={async ( + loadExecutionKPIAggregationProps: LoadExecutionKPIAggregationsProps + ) => + loadExecutionKPIAggregations({ + ...loadExecutionKPIAggregationProps, + http, + }) + } + loadGlobalExecutionKPIAggregations={async ( + loadGlobalExecutionKPIAggregationsProps: LoadGlobalExecutionKPIAggregationsProps + ) => + loadGlobalExecutionKPIAggregations({ + ...loadGlobalExecutionKPIAggregationsProps, + http, + }) + } resolveRule={async (ruleId: Rule['id']) => resolveRule({ http, ruleId })} getHealth={async () => alertingFrameworkHealth({ http })} snoozeRule={async (rule: Rule, snoozeSchedule: SnoozeSchedule) => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx index bfe337a1fb88..5eac73c4e87a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx @@ -419,8 +419,8 @@ describe('tabbed content', () => { tabbedContent.update(); }); - expect(tabbedContent.find('[aria-labelledby="rule_event_log_list"]').exists()).toBeTruthy(); - expect(tabbedContent.find('[aria-labelledby="rule_alert_list"]').exists()).toBeFalsy(); + expect(tabbedContent.find('[aria-labelledby="rule_event_log_list"]').exists()).toBeFalsy(); + expect(tabbedContent.find('[aria-labelledby="rule_alert_list"]').exists()).toBeTruthy(); tabbedContent.find('[data-test-subj="eventLogListTab"]').simulate('click'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index 5c44b9161b2c..db82f36cbc90 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -97,6 +97,14 @@ export function RuleComponent({ }; const tabs = [ + { + id: ALERT_LIST_TAB, + name: i18n.translate('xpack.triggersActionsUI.sections.ruleDetails.rule.alertsTabText', { + defaultMessage: 'Alerts', + }), + 'data-test-subj': 'ruleAlertListTab', + content: renderRuleAlertList(), + }, { id: EVENT_LOG_LIST_TAB, name: i18n.translate('xpack.triggersActionsUI.sections.ruleDetails.rule.eventLogTabText', { @@ -118,14 +126,6 @@ export function RuleComponent({ requestRefresh, }), }, - { - id: ALERT_LIST_TAB, - name: i18n.translate('xpack.triggersActionsUI.sections.ruleDetails.rule.alertsTabText', { - defaultMessage: 'Alerts', - }), - 'data-test-subj': 'ruleAlertListTab', - content: renderRuleAlertList(), - }, ]; const renderTabs = () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.test.tsx new file mode 100644 index 000000000000..f77494e4f3c1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.test.tsx @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { act } from 'react-dom/test-utils'; +import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { loadExecutionKPIAggregations } from '../../../lib/rule_api/load_execution_kpi_aggregations'; +import { loadGlobalExecutionKPIAggregations } from '../../../lib/rule_api/load_global_execution_kpi_aggregations'; +import { RuleEventLogListKPI } from './rule_event_log_list_kpi'; + +jest.mock('../../../../common/lib/kibana', () => ({ + useKibana: jest.fn().mockReturnValue({ + services: { + notifications: { toast: { addDanger: jest.fn() } }, + }, + }), +})); + +jest.mock('../../../lib/rule_api/load_execution_kpi_aggregations', () => ({ + loadExecutionKPIAggregations: jest.fn(), +})); + +jest.mock('../../../lib/rule_api/load_global_execution_kpi_aggregations', () => ({ + loadGlobalExecutionKPIAggregations: jest.fn(), +})); + +const mockKpiResponse = { + success: 4, + unknown: 10, + failure: 60, + activeAlerts: 100, + newAlerts: 40, + recoveredAlerts: 30, + erroredActions: 60, + triggeredActions: 140, +}; + +const loadExecutionKPIAggregationsMock = + loadExecutionKPIAggregations as unknown as jest.MockedFunction; +const loadGlobalExecutionKPIAggregationsMock = + loadGlobalExecutionKPIAggregations as unknown as jest.MockedFunction; + +describe('rule_event_log_list_kpi', () => { + beforeEach(() => { + jest.clearAllMocks(); + loadExecutionKPIAggregationsMock.mockResolvedValue(mockKpiResponse); + loadGlobalExecutionKPIAggregationsMock.mockResolvedValue(mockKpiResponse); + }); + + it('renders correctly', async () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="centerJustifiedSpinner"]').exists()).toBeTruthy(); + + // Let the load resolve + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find('[data-test-subj="centerJustifiedSpinner"]').exists()).toBeFalsy(); + + expect(loadExecutionKPIAggregationsMock).toHaveBeenCalledWith( + expect.objectContaining({ + id: '123', + message: undefined, + outcomeFilter: undefined, + }) + ); + + expect(loadGlobalExecutionKPIAggregations).not.toHaveBeenCalled(); + + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-successOutcome"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.success}`); + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-unknownOutcome"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.unknown}`); + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-failureOutcome"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.failure}`); + expect( + wrapper.find('[data-test-subj="ruleEventLogKpi-activeAlerts"] .euiStat__title').first().text() + ).toEqual(`${mockKpiResponse.activeAlerts}`); + expect( + wrapper.find('[data-test-subj="ruleEventLogKpi-newAlerts"] .euiStat__title').first().text() + ).toEqual(`${mockKpiResponse.newAlerts}`); + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-recoveredAlerts"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.recoveredAlerts}`); + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-erroredActions"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.erroredActions}`); + expect( + wrapper + .find('[data-test-subj="ruleEventLogKpi-triggeredActions"] .euiStat__title') + .first() + .text() + ).toEqual(`${mockKpiResponse.triggeredActions}`); + }); + + it('calls global KPI API if provided global rule id', async () => { + const wrapper = mountWithIntl( + + ); + // Let the load resolve + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(loadGlobalExecutionKPIAggregations).toHaveBeenCalledWith( + expect.objectContaining({ + id: '*', + message: undefined, + outcomeFilter: undefined, + }) + ); + + expect(loadExecutionKPIAggregationsMock).not.toHaveBeenCalled(); + }); + + it('calls KPI API with filters', async () => { + const wrapper = mountWithIntl( + + ); + + // Let the load resolve + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(loadExecutionKPIAggregationsMock).toHaveBeenCalledWith( + expect.objectContaining({ + id: '123', + message: 'test', + outcomeFilter: ['status: 123', 'test:456'], + }) + ); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.tsx new file mode 100644 index 000000000000..7fe7dd8fdb02 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_kpi.tsx @@ -0,0 +1,251 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { useEffect, useState, useMemo, useRef } from 'react'; +import { i18n } from '@kbn/i18n'; +import datemath from '@kbn/datemath'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiIconTip, EuiStat, EuiSpacer } from '@elastic/eui'; +import { IExecutionKPIResult } from '@kbn/alerting-plugin/common'; +import { + ComponentOpts as RuleApis, + withBulkRuleOperations, +} from '../../common/components/with_bulk_rule_api_operations'; +import { useKibana } from '../../../../common/lib/kibana'; +import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; +import { RuleEventLogListStatus } from './rule_event_log_list_status'; + +const getParsedDate = (date: string) => { + if (date.includes('now')) { + return datemath.parse(date)?.format() || date; + } + return date; +}; + +const API_FAILED_MESSAGE = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleEventLogListKpi.apiError', + { + defaultMessage: 'Failed to fetch event log KPI.', + } +); + +const RESPONSE_TOOLTIP = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleEventLogListKpi.responseTooltip', + { + defaultMessage: 'The responses for the latest rule runs.', + } +); + +const ALERTS_TOOLTIP = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleEventLogListKpi.alertsTooltip', + { + defaultMessage: 'The alert statuses for the latest rule runs.', + } +); + +const ACTIONS_TOOLTIP = i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleEventLogListKpi.actionsTooltip', + { + defaultMessage: 'The action statuses for the latest rule runs.', + } +); + +const Stat = ({ + title, + tooltip, + children, +}: { + title: string; + tooltip: string; + children?: JSX.Element; +}) => { + return ( + + + + {title} + + + + + + + {children} + + ); +}; + +export type RuleEventLogListKPIProps = { + ruleId: string; + dateStart: string; + dateEnd: string; + outcomeFilter?: string[]; + message?: string; + refreshToken?: number; +} & Pick; + +export const RuleEventLogListKPI = (props: RuleEventLogListKPIProps) => { + const { + ruleId, + dateStart, + dateEnd, + outcomeFilter, + message, + refreshToken, + loadExecutionKPIAggregations, + loadGlobalExecutionKPIAggregations, + } = props; + const { + notifications: { toasts }, + } = useKibana().services; + + const isInitialized = useRef(false); + + const [isLoading, setIsLoading] = useState(false); + const [kpi, setKpi] = useState(); + + const loadKPIFn = useMemo(() => { + if (ruleId === '*') { + return loadGlobalExecutionKPIAggregations; + } + return loadExecutionKPIAggregations; + }, [ruleId, loadExecutionKPIAggregations, loadGlobalExecutionKPIAggregations]); + + const loadKPIs = async () => { + setIsLoading(true); + try { + const newKpi = await loadKPIFn({ + id: ruleId, + dateStart: getParsedDate(dateStart), + dateEnd: getParsedDate(dateEnd), + outcomeFilter, + message, + }); + setKpi(newKpi); + } catch (e) { + toasts.addDanger({ + title: API_FAILED_MESSAGE, + text: e.body?.message ?? e, + }); + } + setIsLoading(false); + }; + + useEffect(() => { + loadKPIs(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ruleId, dateStart, dateEnd, outcomeFilter, message]); + + useEffect(() => { + if (isInitialized.current) { + loadKPIs(); + } + isInitialized.current = true; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [refreshToken]); + + if (isLoading || !kpi) { + return ; + } + + const getStatDescription = (element: React.ReactNode) => { + return ( + <> + {element} + + + ); + }; + + return ( + + + + + + )} + titleSize="s" + title={kpi.success} + /> + + + )} + titleSize="s" + title={kpi.unknown} + /> + + + )} + titleSize="s" + title={kpi.failure} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export const RuleEventLogListKPIWithApi = withBulkRuleOperations(RuleEventLogListKPI); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx index b647442a8eaf..2c1d6df71fc3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx @@ -30,9 +30,9 @@ import { RuleEventLogListStatusFilter } from './rule_event_log_list_status_filte import { RuleEventLogDataGrid } from './rule_event_log_data_grid'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; import { RuleActionErrorLogFlyout } from './rule_action_error_log_flyout'; - import { RefineSearchPrompt } from '../refine_search_prompt'; import { LoadExecutionLogAggregationsProps } from '../../../lib/rule_api'; +import { RuleEventLogListKPIWithApi as RuleEventLogListKPI } from './rule_event_log_list_kpi'; import { ComponentOpts as RuleApis, withBulkRuleOperations, @@ -114,6 +114,9 @@ export const RuleEventLogListTable = ( const [search, setSearch] = useState(''); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); const [selectedRunLog, setSelectedRunLog] = useState(); + const [internalRefreshToken, setInternalRefreshToken] = useState( + refreshToken + ); // Data grid states const [logs, setLogs] = useState(); @@ -243,6 +246,7 @@ export const RuleEventLogListTable = ( ); const onRefresh = () => { + setInternalRefreshToken(Date.now()); loadEventLogs(); }; @@ -339,6 +343,10 @@ export const RuleEventLogListTable = ( localStorage.setItem(localStorageKey, JSON.stringify(visibleColumns)); }, [localStorageKey, visibleColumns]); + useEffect(() => { + setInternalRefreshToken(refreshToken); + }, [refreshToken]); + return ( <> @@ -371,6 +379,15 @@ export const RuleEventLogListTable = ( + + {renderList()} {isOnLastPage && ( { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + it('should return KPI only from the current space', async () => { + const startTime = new Date().toISOString(); + + const spaceId = UserAtSpaceScenarios[1].space.id; + const user = UserAtSpaceScenarios[1].user; + const response = await supertest + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(spaceId, ruleId, 'rule', 'alerting'); + + const response2 = await supertest + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response2.status).to.eql(200); + const ruleId2 = response2.body.id; + objectRemover.add(spaceId, ruleId2, 'rule', 'alerting'); + + await retry.try(async () => { + // break AAD + await supertest + .put(`${getUrlPrefix(spaceId)}/api/alerts_fixture/saved_object/alert/${ruleId2}`) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + }); + + await retry.try(async () => { + // there can be a successful execute before the error one + const someEvents = await getEventLog({ + getService, + spaceId, + type: 'alert', + id: ruleId2, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + const errorEvents = someEvents.filter( + (event) => event?.kibana?.alerting?.status === 'error' + ); + expect(errorEvents.length).to.be.above(0); + }); + + await retry.try(async () => { + // there can be a successful execute before the error one + const logResponse = await supertestWithoutAuth + .get( + `${getUrlPrefix( + spaceId + )}/internal/alerting/_global_execution_logs?date_start=${startTime}&date_end=9999-12-31T23:59:59Z&per_page=50&page=1` + ) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + expect(logResponse.statusCode).to.be(200); + expect(logResponse.body.data.length).to.be.above(1); + }); + + await retry.try(async () => { + // break AAD + await supertest + .put(`${getUrlPrefix(spaceId)}/api/alerts_fixture/saved_object/alert/${ruleId}`) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + }); + + await retry.try(async () => { + // there can be a successful execute before the error one + const someEvents = await getEventLog({ + getService, + spaceId, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + const errorEvents = someEvents.filter( + (event) => event?.kibana?.alerting?.status === 'error' + ); + expect(errorEvents.length).to.be.above(0); + }); + + const kpiLogs = await retry.try(async () => { + // there can be a successful execute before the error one + const logResponse = await supertestWithoutAuth + .get( + `${getUrlPrefix( + spaceId + )}/internal/alerting/_global_execution_kpi?date_start=${startTime}&date_end=9999-12-31T23:59:59Z` + ) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + expect(logResponse.statusCode).to.be(200); + + return logResponse.body; + }); + + expect(Object.keys(kpiLogs)).to.eql([ + 'success', + 'unknown', + 'failure', + 'activeAlerts', + 'newAlerts', + 'recoveredAlerts', + 'erroredActions', + 'triggeredActions', + ]); + // it should be above 1 since we have two rule running + expect(kpiLogs.success).to.be.above(1); + expect(kpiLogs.failure).to.be.above(0); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/get_rule_execution_kpi.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/get_rule_execution_kpi.ts new file mode 100644 index 000000000000..2303fc616d8d --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/get_rule_execution_kpi.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function getRuleExecutionKpiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + const retry = getService('retry'); + + describe('getRuleExecutionKpi', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + it('should return KPI only from the current space', async () => { + const startTime = new Date().toISOString(); + + const spaceId = UserAtSpaceScenarios[1].space.id; + const user = UserAtSpaceScenarios[1].user; + const response = await supertest + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(spaceId, ruleId, 'rule', 'alerting'); + + const spaceId2 = UserAtSpaceScenarios[4].space.id; + const response2 = await supertest + .post(`${getUrlPrefix(spaceId2)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + }) + ); + + expect(response2.status).to.eql(200); + const ruleId2 = response2.body.id; + objectRemover.add(spaceId2, ruleId2, 'rule', 'alerting'); + + await retry.try(async () => { + // there can be a successful execute before the error one + const someEvents = await getEventLog({ + getService, + spaceId, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + + expect(someEvents.length).to.be.above(0); + }); + + await retry.try(async () => { + // break AAD + await supertest + .put(`${getUrlPrefix(spaceId)}/api/alerts_fixture/saved_object/alert/${ruleId}`) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + }); + + await retry.try(async () => { + // there can be a successful execute before the error one + const someEvents = await getEventLog({ + getService, + spaceId, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + const errorEvents = someEvents.filter( + (event) => event?.kibana?.alerting?.status === 'error' + ); + expect(errorEvents.length).to.be.above(0); + }); + + const kpiLogs = await retry.try(async () => { + // there can be a successful execute before the error one + const logResponse = await supertestWithoutAuth + .get( + `${getUrlPrefix( + spaceId + )}/internal/alerting/rule/${ruleId}/_execution_kpi?date_start=${startTime}&date_end=9999-12-31T23:59:59Z` + ) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + expect(logResponse.statusCode).to.be(200); + + return logResponse.body; + }); + + expect(Object.keys(kpiLogs)).to.eql([ + 'success', + 'unknown', + 'failure', + 'activeAlerts', + 'newAlerts', + 'recoveredAlerts', + 'erroredActions', + 'triggeredActions', + ]); + expect(kpiLogs.success).to.be.above(0); + expect(kpiLogs.failure).to.be.above(0); + }); + }); +} From 254b9a525d5508614d035a22dc690f53ed837a2e Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 28 Sep 2022 15:52:01 +0300 Subject: [PATCH 062/185] [Lens] Stabilize the table functional test (#142038) --- .../test/functional/apps/lens/group1/table.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/test/functional/apps/lens/group1/table.ts b/x-pack/test/functional/apps/lens/group1/table.ts index 7bf9b49c53d8..724efbe6c6c8 100644 --- a/x-pack/test/functional/apps/lens/group1/table.ts +++ b/x-pack/test/functional/apps/lens/group1/table.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const PageObjects = getPageObjects(['visualize', 'lens', 'common']); const listingTable = getService('listingTable'); const find = getService('find'); const retry = getService('retry'); @@ -91,14 +91,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow to sort by transposed columns', async () => { await PageObjects.lens.changeTableSortingBy(2, 'ascending'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); expect(await PageObjects.lens.getDatatableCellText(0, 2)).to.eql('17,246'); }); it('should show dynamic coloring feature for numeric columns', async () => { await PageObjects.lens.openDimensionEditor('lnsDatatable_metrics > lns-dimensionTrigger'); await PageObjects.lens.setTableDynamicColoring('text'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); const styleObj = await PageObjects.lens.getDatatableCellStyle(0, 2); expect(styleObj['background-color']).to.be(undefined); expect(styleObj.color).to.be('rgb(133, 189, 177)'); @@ -106,7 +106,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow to color cell background rather than text', async () => { await PageObjects.lens.setTableDynamicColoring('cell'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); const styleObj = await PageObjects.lens.getDatatableCellStyle(0, 2); expect(styleObj['background-color']).to.be('rgb(133, 189, 177)'); // should also set text color when in cell mode @@ -115,9 +115,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should open the palette panel to customize the palette look', async () => { await PageObjects.lens.openPalettePanel('lnsDatatable'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); await PageObjects.lens.changePaletteTo('temperature'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); const styleObj = await PageObjects.lens.getDatatableCellStyle(0, 2); expect(styleObj['background-color']).to.be('rgb(235, 239, 245)'); }); @@ -125,7 +125,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should keep the coloring consistent when changing mode', async () => { // Change mode from percent to number await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); // check that all remained the same const styleObj = await PageObjects.lens.getDatatableCellStyle(0, 2); expect(styleObj['background-color']).to.be('rgb(235, 239, 245)'); @@ -133,7 +133,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should keep the coloring consistent when moving to custom palette from default', async () => { await PageObjects.lens.changePaletteTo('custom'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); // check that all remained the same const styleObj = await PageObjects.lens.getDatatableCellStyle(0, 2); expect(styleObj['background-color']).to.be('rgb(235, 239, 245)'); @@ -149,7 +149,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); // when clicking on another row will trigger a sorting + update await testSubjects.click('lnsPalettePanel_dynamicColoring_range_value_1'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); // pick a cell without color as is below the range const styleObj = await PageObjects.lens.getDatatableCellStyle(3, 3); expect(styleObj['background-color']).to.be(undefined); @@ -159,7 +159,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow the user to reverse the palette', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_reverseColors'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); const styleObj = await PageObjects.lens.getDatatableCellStyle(1, 1); expect(styleObj['background-color']).to.be('rgb(168, 191, 218)'); // should also set text color when in cell mode @@ -169,7 +169,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow to show a summary table for metric columns', async () => { await PageObjects.lens.setTableSummaryRowFunction('sum'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.waitForVisualization(); await PageObjects.lens.assertExactText( '[data-test-subj="lnsDataTable-footer-169.228.188.120-›-Average-of-bytes"]', 'Sum: 18,994' From c7301e51f172028bdce87ea626b25c15849fd7eb Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Wed, 28 Sep 2022 13:58:57 +0100 Subject: [PATCH 063/185] [Security Solution][Detections] adds missing bulk edit data view tests (#141915) ## Summary - addresses https://github.com/elastic/kibana/issues/135201 - adds Data View cypress and integration tests according to [Data view Bulk Edit test plan](https://docs.google.com/document/d/116x7ITTTJQ6cTiwaGK831_f6Ox7XB3qyLiHxC3Cmf8w/edit#heading=h.j583i3o7bg2g) (internal document) - integration tests were added earlier in https://github.com/elastic/kibana/pull/138448 --- .../bulk_edit_rules_data_view.cy.ts | 177 ++++++++++++++ .../cypress/screens/rules_bulk_edit.ts | 6 + .../cypress/tasks/api_calls/rules.ts | 228 +++++++++--------- .../cypress/tasks/rule_details.ts | 3 + .../cypress/tasks/rules_bulk_edit.ts | 9 + .../group10/perform_bulk_action.ts | 36 +++ 6 files changed, 344 insertions(+), 115 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts new file mode 100644 index 000000000000..766c8d9483f0 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { + RULES_BULK_EDIT_DATA_VIEWS_WARNING, + RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX, +} from '../../screens/rules_bulk_edit'; + +import { DATA_VIEW_DETAILS, INDEX_PATTERNS_DETAILS } from '../../screens/rule_details'; + +import { + waitForRulesTableToBeLoaded, + goToRuleDetails, + selectNumberOfRules, +} from '../../tasks/alerts_detection_rules'; + +import { + typeIndexPatterns, + waitForBulkEditActionToFinish, + submitBulkEditForm, + checkOverwriteDataViewCheckbox, + checkOverwriteIndexPatternsCheckbox, + openBulkEditAddIndexPatternsForm, + openBulkEditDeleteIndexPatternsForm, +} from '../../tasks/rules_bulk_edit'; + +import { hasIndexPatterns, getDetails, assertDetailsNotExist } from '../../tasks/rule_details'; +import { login, visitWithoutDateRange } from '../../tasks/login'; + +import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; +import { + createCustomRule, + createCustomIndicatorRule, + createEventCorrelationRule, + createThresholdRule, + createNewTermsRule, + createSavedQueryRule, +} from '../../tasks/api_calls/rules'; +import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../tasks/common'; + +import { + getEqlRule, + getNewThreatIndicatorRule, + getNewRule, + getNewThresholdRule, + getNewTermsRule, +} from '../../objects/rule'; + +import { esArchiverResetKibana } from '../../tasks/es_archiver'; + +const DATA_VIEW_ID = 'auditbeat'; + +const expectedIndexPatterns = ['index-1-*', 'index-2-*']; + +const expectedNumberOfCustomRulesToBeEdited = 6; + +const indexDataSource = { dataView: DATA_VIEW_ID, type: 'dataView' } as const; + +const defaultRuleData = { + dataSource: indexDataSource, +}; + +describe('Detection rules, bulk edit, data view', () => { + before(() => { + cleanKibana(); + login(); + }); + beforeEach(() => { + deleteAlertsAndRules(); + esArchiverResetKibana(); + + postDataView(DATA_VIEW_ID); + + createCustomRule({ ...getNewRule(), ...defaultRuleData }, '1'); + createEventCorrelationRule({ ...getEqlRule(), ...defaultRuleData }, '2'); + createCustomIndicatorRule({ ...getNewThreatIndicatorRule(), ...defaultRuleData }, '3'); + createThresholdRule({ ...getNewThresholdRule(), ...defaultRuleData }, '4'); + createNewTermsRule({ ...getNewTermsRule(), ...defaultRuleData }, '5'); + createSavedQueryRule({ ...getNewRule(), ...defaultRuleData, savedId: 'mocked' }, '6'); + + visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + + waitForRulesTableToBeLoaded(); + }); + + it('Add index patterns to custom rules with configured data view', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited }); + + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + assertDetailsNotExist(INDEX_PATTERNS_DETAILS); + }); + + it('Add index patterns to custom rules with configured data view when data view checkbox is checked', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + + // click on data view overwrite checkbox, ensure warning is displayed + cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('not.exist'); + checkOverwriteDataViewCheckbox(); + cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('be.visible'); + + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited }); + + // check if rule has been updated with index patterns and data view does not exist + goToRuleDetails(); + hasIndexPatterns(expectedIndexPatterns.join('')); + assertDetailsNotExist(DATA_VIEW_DETAILS); + }); + + it('Overwrite index patterns in custom rules with configured data view', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + checkOverwriteIndexPatternsCheckbox(); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited }); + + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + assertDetailsNotExist(INDEX_PATTERNS_DETAILS); + }); + + it('Overwrite index patterns in custom rules with configured data view when data view checkbox is checked', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + checkOverwriteIndexPatternsCheckbox(); + checkOverwriteDataViewCheckbox(); + + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited }); + + // check if rule has been overwritten with index patterns and data view does not exist + goToRuleDetails(); + hasIndexPatterns(expectedIndexPatterns.join('')); + assertDetailsNotExist(DATA_VIEW_DETAILS); + }); + + it('Delete index patterns in custom rules with configured data view', () => { + selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + + openBulkEditDeleteIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + + // in delete form data view checkbox is absent + cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX).should('not.exist'); + + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited }); + + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts index 34e4f9515b27..6f4056034a05 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts @@ -32,6 +32,9 @@ export const RULES_BULK_EDIT_INDEX_PATTERNS = '[data-test-subj="bulkEditRulesInd export const RULES_BULK_EDIT_OVERWRITE_INDEX_PATTERNS_CHECKBOX = '[data-test-subj="bulkEditRulesOverwriteIndexPatterns"]'; +export const RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX = + '[data-test-subj="bulkEditRulesOverwriteRulesWithDataViews"]'; + export const RULES_BULK_EDIT_TAGS = '[data-test-subj="bulkEditRulesTags"]'; export const RULES_BULK_EDIT_OVERWRITE_TAGS_CHECKBOX = @@ -48,6 +51,9 @@ export const RULES_BULK_EDIT_TIMELINE_TEMPLATES_SELECTOR = export const RULES_BULK_EDIT_TIMELINE_TEMPLATES_WARNING = '[data-test-subj="bulkEditRulesTimelineTemplateWarning"]'; +export const RULES_BULK_EDIT_DATA_VIEWS_WARNING = + '[data-test-subj="bulkEditRulesDataViewsWarning"]'; + export const RULES_BULK_EDIT_SCHEDULES_WARNING = '[data-test-subj="bulkEditRulesSchedulesWarning"]'; export const UPDATE_SCHEDULE_INTERVAL_INPUT = diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 4fb076f11f44..80fb77013acb 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -53,7 +53,8 @@ export const createCustomRule = ( severity: rule.severity.toLocaleLowerCase(), type: 'query', from: 'now-50000h', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : '', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, query: rule.customQuery, language: 'kuery', enabled: false, @@ -71,83 +72,80 @@ export const createCustomRule = ( }); export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_testing') => { - if (rule.dataSource.type === 'indexPatterns') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), - description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, - name: rule.name, - severity: rule.severity.toLocaleLowerCase(), - type: 'eql', - index: rule.dataSource.index, - query: rule.customQuery, - language: 'eql', - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); - } + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, + from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'eql', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, + query: rule.customQuery, + language: 'eql', + enabled: true, + tags: rule.tags, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); }; export const createThresholdRule = (rule: ThresholdRule, ruleId = 'rule_testing') => { - if (rule.dataSource.type === 'indexPatterns') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), - description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, - name: rule.name, - severity: rule.severity.toLocaleLowerCase(), - type: 'threshold', - index: rule.dataSource.index, - query: rule.customQuery, - threshold: { - field: [rule.thresholdField], - value: parseInt(rule.threshold, 10), - cardinality: [], - }, - enabled: true, - tags: rule.tags, + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, + from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'threshold', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, + query: rule.customQuery, + threshold: { + field: [rule.thresholdField], + value: parseInt(rule.threshold, 10), + cardinality: [], }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); - } + enabled: true, + tags: rule.tags, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); }; export const createNewTermsRule = (rule: NewTermsRule, ruleId = 'rule_testing') => { - if (rule.dataSource.type === 'indexPatterns') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), - description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, - name: rule.name, - severity: rule.severity.toLocaleLowerCase(), - type: 'new_terms', - index: rule.dataSource.index, - query: rule.customQuery, - new_terms_fields: rule.newTermsFields, - history_window_start: `now-${rule.historyWindowSize.interval}${rule.historyWindowSize.type}`, - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); - } + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, + from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'new_terms', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, + query: rule.customQuery, + new_terms_fields: rule.newTermsFields, + history_window_start: `now-${rule.historyWindowSize.interval}${rule.historyWindowSize.type}`, + enabled: true, + tags: rule.tags, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); }; export const createSavedQueryRule = ( @@ -166,7 +164,8 @@ export const createSavedQueryRule = ( severity: rule.severity.toLocaleLowerCase(), type: 'saved_query', from: 'now-50000h', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : '', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, saved_id: rule.savedId, language: 'kuery', enabled: false, @@ -184,49 +183,48 @@ export const createSavedQueryRule = ( }); export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') => { - if (rule.dataSource.type === 'indexPatterns') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), - description: rule.description, - // Default interval is 1m, our tests config overwrite this to 1s - // See https://github.com/elastic/kibana/pull/125396 for details - interval: '10s', - name: rule.name, - severity: rule.severity.toLocaleLowerCase(), - type: 'threat_match', - timeline_id: rule.timeline.templateTimelineId, - timeline_title: rule.timeline.title, - threat_mapping: [ - { - entries: [ - { - field: rule.indicatorMappingField, - type: 'mapping', - value: rule.indicatorIndexField, - }, - ], - }, - ], - threat_query: '*:*', - threat_language: 'kuery', - threat_filters: [], - threat_index: rule.indicatorIndexPattern, - threat_indicator_path: rule.threatIndicatorPath, - from: 'now-50000h', - index: rule.dataSource.index, - query: rule.customQuery || '*:*', - language: 'kuery', - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); - } + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + // Default interval is 1m, our tests config overwrite this to 1s + // See https://github.com/elastic/kibana/pull/125396 for details + interval: '10s', + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'threat_match', + timeline_id: rule.timeline.templateTimelineId, + timeline_title: rule.timeline.title, + threat_mapping: [ + { + entries: [ + { + field: rule.indicatorMappingField, + type: 'mapping', + value: rule.indicatorIndexField, + }, + ], + }, + ], + threat_query: '*:*', + threat_language: 'kuery', + threat_filters: [], + threat_index: rule.indicatorIndexPattern, + threat_indicator_path: rule.threatIndicatorPath, + from: 'now-50000h', + index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, + data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, + query: rule.customQuery || '*:*', + language: 'kuery', + enabled: true, + tags: rule.tags, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); }; export const createCustomRuleEnabled = ( diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 75668b49aa20..0bd06911acf7 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -158,6 +158,9 @@ export const goBackToAllRulesTable = () => { export const getDetails = (title: string | RegExp) => cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); +export const assertDetailsNotExist = (title: string | RegExp) => + cy.get(DETAILS_TITLE).contains(title).should('not.exist'); + export const hasIndexPatterns = (indexPatterns: string) => { cy.get(DEFINITION_DETAILS).within(() => { getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns); diff --git a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts index 5b3ae403f4a0..0000a84f2668 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts @@ -30,6 +30,7 @@ import { APPLY_TIMELINE_RULE_BULK_MENU_ITEM, RULES_BULK_EDIT_OVERWRITE_TAGS_CHECKBOX, RULES_BULK_EDIT_OVERWRITE_INDEX_PATTERNS_CHECKBOX, + RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX, RULES_BULK_EDIT_TIMELINE_TEMPLATES_SELECTOR, UPDATE_SCHEDULE_MENU_ITEM, UPDATE_SCHEDULE_INTERVAL_INPUT, @@ -159,6 +160,14 @@ export const checkOverwriteIndexPatternsCheckbox = () => { .should('be.checked'); }; +export const checkOverwriteDataViewCheckbox = () => { + cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX) + .should('have.text', 'Apply changes to rules configured with data views') + .click() + .get('input') + .should('be.checked'); +}; + export const selectTimelineTemplate = (timelineTitle: string) => { cy.get(RULES_BULK_EDIT_TIMELINE_TEMPLATES_SELECTOR).click(); cy.get(TIMELINE_SEARCHBOX).type(`${timelineTitle}{enter}`).should('not.exist'); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index c17e679b6be3..8f06bc93820a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -1829,6 +1829,42 @@ export default ({ getService }: FtrProviderContext): void => { expect(setIndexRule.data_view_id).to.eql(undefined); }); + it('should return error when set an empty index pattern to a rule and overwrite the data view when overwrite_data_views is true', async () => { + const dataViewId = 'index1-*'; + const simpleRule = { + ...getSimpleRule(), + index: undefined, + data_view_id: dataViewId, + }; + const rule = await createRule(supertest, log, simpleRule); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkAction.edit, + [BulkAction.edit]: [ + { + type: BulkActionEditType.set_index_patterns, + value: [], + overwrite_data_views: true, + }, + ], + }) + .expect(500); + + expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 }); + expect(body.attributes.errors[0]).to.eql({ + message: "Mutated params invalid: Index patterns can't be empty", + status_code: 500, + rules: [ + { + id: rule.id, + name: rule.name, + }, + ], + }); + }); + it('should NOT set an index pattern to a rule and overwrite the data view when overwrite_data_views is false', async () => { const ruleId = 'ruleId'; const dataViewId = 'index1-*'; From 7cae4515534d8387c67cc8f114573ed241095106 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 14:25:28 +0100 Subject: [PATCH 064/185] skip flaky suite (#133259) --- x-pack/test/api_integration/apis/osquery/packs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/osquery/packs.ts b/x-pack/test/api_integration/apis/osquery/packs.ts index 9d00249a4e1b..de490a489cec 100644 --- a/x-pack/test/api_integration/apis/osquery/packs.ts +++ b/x-pack/test/api_integration/apis/osquery/packs.ts @@ -45,7 +45,8 @@ limit 1000;`; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('Packs', () => { + // FLAKY: https://github.com/elastic/kibana/issues/133259 + describe.skip('Packs', () => { let packId: string = ''; let hostedPolicy: Record; let packagePolicyId: string; From 421150a445e5811728620bd0c887188d830cc0be Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 14:27:39 +0100 Subject: [PATCH 065/185] skip flaky suite (#142083) --- .../apps/ml/data_frame_analytics/outlier_detection_creation.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 947cd82cdd34..b3c471f7255c 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -13,7 +13,8 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - describe('outlier detection creation', function () { + // FLAKY: https://github.com/elastic/kibana/issues/142083 + describe.skip('outlier detection creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); From 6ae4764db6ed54ac8ffc690ac4f1c4c81ad176e1 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 28 Sep 2022 08:46:21 -0500 Subject: [PATCH 066/185] Updates warning for unsupported rolled up data function (#141524) * Updates warning for unsupported rolled up data function * Update the functional test * Fix text on functional test Co-authored-by: Stratoula Kalafateli --- x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx | 2 +- x-pack/test/functional/apps/lens/group2/tsdb.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx index 2cb1ba164d57..77a359729a5e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx @@ -194,7 +194,7 @@ export function getTSDBRollupWarningMessages( ).map((label) => i18n.translate('xpack.lens.indexPattern.tsdbRollupWarning', { defaultMessage: - '"{label}" does not work for all indices in the selected data view because it\'s using a function which is not supported on rolled up data. Please edit the visualization to use another function or change the time range.', + '{label} uses a function that is unsupported by rolled up data. Select a different function or change the time range.', values: { label, }, diff --git a/x-pack/test/functional/apps/lens/group2/tsdb.ts b/x-pack/test/functional/apps/lens/group2/tsdb.ts index 7a43fc47471a..d19ab9d19db7 100644 --- a/x-pack/test/functional/apps/lens/group2/tsdb.ts +++ b/x-pack/test/functional/apps/lens/group2/tsdb.ts @@ -119,7 +119,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('lns-indexPatternDimension-median'); await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.assertEditorWarning( - '"Median of kubernetes.container.memory.available.bytes" does not work for all indices in the selected data view because it\'s using a function which is not supported on rolled up data. Please edit the visualization to use another function or change the time range.' + 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.' ); }); it('shows warnings in dashboards as well', async () => { @@ -127,7 +127,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); await PageObjects.lens.assertInlineWarning( - '"Median of kubernetes.container.memory.available.bytes" does not work for all indices in the selected data view because it\'s using a function which is not supported on rolled up data. Please edit the visualization to use another function or change the time range.' + 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.' ); }); it('still shows other warnings as toast', async () => { From a73c2d4a1f7e358bac729277c3257dcb499026fc Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 15:51:23 +0100 Subject: [PATCH 067/185] skip flaky suite (#142083) --- .../apps/ml/data_frame_analytics/outlier_detection_creation.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index b3c471f7255c..fb04cd793d9d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -109,7 +109,8 @@ export default function ({ getService }: FtrProviderContext) { ]; for (const testData of testDataList) { - describe(`${testData.suiteTitle}`, function () { + // FLAKY: https://github.com/elastic/kibana/issues/142083 + describe.skip(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); From 04f570c1a577ab93644e5b802c52fbb4185d90a3 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 16:20:06 +0100 Subject: [PATCH 068/185] skip flaky suite (#142095) --- .../apps/ml/data_frame_analytics/regression_creation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts index 7a84c41aa4a6..744b61a2a06c 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts @@ -13,7 +13,8 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - describe('regression creation', function () { + // FLAKY: https://github.com/elastic/kibana/issues/142095 + describe.skip('regression creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression'); await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression', '@timestamp'); @@ -86,7 +87,8 @@ export default function ({ getService }: FtrProviderContext) { ]; for (const testData of testDataList) { - describe(`${testData.suiteTitle}`, function () { + // FLAKY: https://github.com/elastic/kibana/issues/142095 + describe.skip(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); From f72002e6851181870450f8d359986120286fe0d1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 16:24:21 +0100 Subject: [PATCH 069/185] skip flaky suite (#142102) --- .../apps/ml/data_frame_analytics/classification_creation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 2ba4ac6f0835..ea9552506270 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -13,7 +13,8 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - describe('classification creation', function () { + // FLAKY: https://github.com/elastic/kibana/issues/142102 + describe.skip('classification creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp'); @@ -92,7 +93,8 @@ export default function ({ getService }: FtrProviderContext) { }, ]; for (const testData of testDataList) { - describe(`${testData.suiteTitle}`, function () { + // FLAKY: https://github.com/elastic/kibana/issues/142102 + describe.skip(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); From 0b4a942a00bcceee19604bfd06c35663f87725cd Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 16:31:36 +0100 Subject: [PATCH 070/185] skip flaky suite (#142118) --- .../test/functional/apps/ml/data_frame_analytics/cloning.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts index 3a33c95edba4..7d6cc5ea9881 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts @@ -15,7 +15,8 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - describe('jobs cloning supported by UI form', function () { + // FLAKY: https://github.com/elastic/kibana/issues/142118 + describe.skip('jobs cloning supported by UI form', function () { const testDataList: Array<{ suiteTitle: string; archive: string; @@ -135,7 +136,8 @@ export default function ({ getService }: FtrProviderContext) { }); for (const testData of testDataList) { - describe(`${testData.suiteTitle}`, function () { + // FLAKY: https://github.com/elastic/kibana/issues/142118 + describe.skip(`${testData.suiteTitle}`, function () { const cloneJobId = `${testData.job.id}_clone`; const cloneDestIndex = `${testData.job!.dest!.index}_clone`; From a47918dcd9ca542c5905ac1626e35e4e796743d1 Mon Sep 17 00:00:00 2001 From: doakalexi <109488926+doakalexi@users.noreply.github.com> Date: Wed, 28 Sep 2022 13:17:50 -0400 Subject: [PATCH 071/185] [ResponseOps][Alerting] xpack.actions.proxyUrl is not validated as a URL at startup (#141970) * Adding validation * Removing error --- x-pack/plugins/actions/server/config.test.ts | 22 ++++++++++++++++++++ x-pack/plugins/actions/server/config.ts | 9 ++++++++ 2 files changed, 31 insertions(+) diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index e6e3a24db521..475b88089581 100644 --- a/x-pack/plugins/actions/server/config.test.ts +++ b/x-pack/plugins/actions/server/config.test.ts @@ -163,6 +163,28 @@ describe('config validation', () => { `); }); + test('validates proxyUrl', () => { + const proxyUrl = 'https://test.com'; + const badProxyUrl = 'bad url'; + let validated: ActionsConfig; + + validated = configSchema.validate({ proxyUrl }); + expect(validated.proxyUrl).toEqual(proxyUrl); + expect(getValidatedConfig(mockLogger, validated).proxyUrl).toEqual(proxyUrl); + expect(mockLogger.warn.mock.calls).toMatchInlineSnapshot(`Array []`); + + validated = configSchema.validate({ proxyUrl: badProxyUrl }); + expect(validated.proxyUrl).toEqual(badProxyUrl); + expect(getValidatedConfig(mockLogger, validated).proxyUrl).toEqual(badProxyUrl); + expect(mockLogger.warn.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "The confguration xpack.actions.proxyUrl: bad url is invalid.", + ], + ] + `); + }); + // Most of the customHostSettings tests are in ./lib/custom_host_settings.test.ts // but this one seemed more relevant for this test suite, since url is the one // required property. diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts index 4c8ca7ff9fff..76270a466ee8 100644 --- a/x-pack/plugins/actions/server/config.ts +++ b/x-pack/plugins/actions/server/config.ts @@ -127,6 +127,15 @@ export type ActionsConfig = TypeOf; export function getValidatedConfig(logger: Logger, originalConfig: ActionsConfig): ActionsConfig { const proxyBypassHosts = originalConfig.proxyBypassHosts; const proxyOnlyHosts = originalConfig.proxyOnlyHosts; + const proxyUrl = originalConfig.proxyUrl; + + if (proxyUrl) { + try { + new URL(proxyUrl); + } catch (err) { + logger.warn(`The confguration xpack.actions.proxyUrl: ${proxyUrl} is invalid.`); + } + } if (proxyBypassHosts && proxyOnlyHosts) { logger.warn( From f76918732e52cf5aa5a87d428ad4035b24e0fd33 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Sep 2022 20:35:25 +0300 Subject: [PATCH 072/185] [Cases] Convert delete and update cases hook to react query (#141920) * Use react query for delete cases * Convert delete to react query * Convert update to react query * Convert use_get_cases_status to react query * Convert use_get_cases_metrics to react query * Refresh metrics and statuses * Show loading when updating cases * Create query key builder * Improve refreshing logic * Improve delete messages * Fix types and tests * Improvements * PR feedback * Fix bug * Fix i18n --- x-pack/plugins/cases/common/ui/types.ts | 12 +- .../cases/public/common/translations.ts | 8 +- .../public/common/use_cases_toast.test.tsx | 355 +++++++++++------- .../cases/public/common/use_cases_toast.tsx | 30 +- .../all_cases/all_cases_list.test.tsx | 204 +++------- .../components/all_cases/all_cases_list.tsx | 62 +-- .../all_cases/cases_metrics.test.tsx | 41 +- .../components/all_cases/cases_metrics.tsx | 25 +- .../public/components/all_cases/columns.tsx | 82 ++-- .../components/all_cases/index.test.tsx | 17 +- .../public/components/all_cases/table.tsx | 9 +- .../all_cases/table_filters.test.tsx | 9 - .../components/all_cases/table_filters.tsx | 15 +- .../components/all_cases/translations.ts | 41 +- .../all_cases/use_is_loading_cases.tsx | 20 + .../all_cases/use_on_refresh_cases.test.tsx | 36 ++ .../all_cases/use_on_refresh_cases.tsx | 28 ++ .../components/all_cases/utility_bar.tsx | 128 +++---- .../public/components/bulk_actions/index.tsx | 2 +- .../case_action_bar/actions.test.tsx | 46 +-- .../components/case_action_bar/actions.tsx | 43 ++- .../components/case_view/index.test.tsx | 8 +- .../use_on_refresh_case_view_page.tsx | 7 +- .../components/confirm_delete_case/index.tsx | 17 +- .../confirm_delete_case/translations.ts | 6 - .../cases/public/containers/__mocks__/api.ts | 7 +- .../cases/public/containers/api.test.tsx | 8 +- x-pack/plugins/cases/public/containers/api.ts | 6 +- .../containers/configure/use_action_types.tsx | 4 +- .../containers/configure/use_connectors.tsx | 4 +- .../cases/public/containers/constants.ts | 45 ++- .../cases/public/containers/translations.ts | 45 +-- .../containers/use_bulk_update_case.test.tsx | 151 +++----- .../containers/use_bulk_update_case.tsx | 171 ++------- .../containers/use_delete_cases.test.tsx | 155 +++----- .../public/containers/use_delete_cases.tsx | 156 ++------ .../containers/use_get_action_license.tsx | 4 +- .../cases/public/containers/use_get_case.tsx | 4 +- .../containers/use_get_case_metrics.tsx | 4 +- .../containers/use_get_case_user_actions.tsx | 4 +- .../cases/public/containers/use_get_cases.tsx | 4 +- .../containers/use_get_cases_metrics.test.tsx | 126 ++----- .../containers/use_get_cases_metrics.tsx | 93 ++--- .../containers/use_get_cases_status.test.tsx | 108 ++---- .../containers/use_get_cases_status.tsx | 99 ++--- .../cases/public/containers/use_get_tags.tsx | 7 +- .../use_bulk_get_user_profiles.ts | 4 +- .../use_get_current_user_profile.ts | 4 +- .../use_suggest_user_profiles.ts | 8 +- .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 52 files changed, 977 insertions(+), 1504 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/all_cases/use_is_loading_cases.tsx create mode 100644 x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.test.tsx create mode 100644 x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.tsx diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 9af253fc9559..65a7fec902f7 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -82,6 +82,7 @@ export type Case = Omit, 'comments'> & { comments export type Cases = Omit, 'cases'> & { cases: Case[] }; export type CasesStatus = SnakeToCamelCase; export type CasesMetrics = SnakeToCamelCase; +export type CaseUpdateRequest = SnakeToCamelCase; export interface ResolvedCase { case: Case; @@ -133,12 +134,6 @@ export interface ApiProps { signal: AbortSignal; } -export interface BulkUpdateStatus { - status: string; - id: string; - version: string; -} - export interface ActionLicense { id: string; name: string; @@ -147,11 +142,6 @@ export interface ActionLicense { enabledInLicense: boolean; } -export interface DeleteCase { - id: string; - title: string; -} - export interface FieldMappings { id: string; title?: string; diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index 1dbc271e9be4..974833987822 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -18,7 +18,7 @@ export const CANCEL = i18n.translate('xpack.cases.caseView.cancel', { export const DELETE_CASE = (quantity: number = 1) => i18n.translate('xpack.cases.confirmDeleteCase.deleteCase', { values: { quantity }, - defaultMessage: `Delete {quantity, plural, =1 {case} other {cases}}`, + defaultMessage: `Delete {quantity, plural, =1 {case} other {{quantity} cases}}`, }); export const NAME = i18n.translate('xpack.cases.caseView.name', { @@ -296,3 +296,9 @@ export const READ_ACTIONS_PERMISSIONS_ERROR_MSG = i18n.translate( 'You do not have permission to view connectors. If you would like to view connectors, contact your Kibana administrator.', } ); + +export const DELETED_CASES = (totalCases: number) => + i18n.translate('xpack.cases.containers.deletedCases', { + values: { totalCases }, + defaultMessage: 'Deleted {totalCases, plural, =1 {case} other {{totalCases} cases}}', + }); diff --git a/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx b/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx index e8661387e1e6..cfa2d3a6e005 100644 --- a/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx +++ b/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx @@ -23,6 +23,7 @@ const useKibanaMock = useKibana as jest.Mocked; describe('Use cases toast hook', () => { const successMock = jest.fn(); + const errorMock = jest.fn(); const getUrlForApp = jest.fn().mockReturnValue(`/app/cases/${mockCase.id}`); const navigateToUrl = jest.fn(); @@ -51,6 +52,7 @@ describe('Use cases toast hook', () => { useToastsMock.mockImplementation(() => { return { addSuccess: successMock, + addError: errorMock, }; }); @@ -63,159 +65,260 @@ describe('Use cases toast hook', () => { }; }); - describe('Toast hook', () => { - it('should create a success toast when invoked with a case', () => { - const { result } = renderHook( - () => { - return useCasesToast(); - }, - { wrapper: TestProviders } - ); - result.current.showSuccessAttach({ - theCase: mockCase, + describe('showSuccessAttach', () => { + describe('Toast hook', () => { + it('should create a success toast when invoked with a case', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ + theCase: mockCase, + }); + expect(successMock).toHaveBeenCalled(); }); - expect(successMock).toHaveBeenCalled(); }); - }); - describe('toast title', () => { - it('should create a success toast when invoked with a case and a custom title', () => { - const { result } = renderHook( - () => { - return useCasesToast(); - }, - { wrapper: TestProviders } - ); - result.current.showSuccessAttach({ theCase: mockCase, title: 'Custom title' }); - validateTitle('Custom title'); - }); + describe('toast title', () => { + it('should create a success toast when invoked with a case and a custom title', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ theCase: mockCase, title: 'Custom title' }); + validateTitle('Custom title'); + }); - it('should display the alert sync title when called with an alert attachment (1 alert)', () => { - const { result } = renderHook( - () => { - return useCasesToast(); - }, - { wrapper: TestProviders } - ); - result.current.showSuccessAttach({ - theCase: mockCase, - attachments: [alertComment as SupportedCaseAttachment], + it('should display the alert sync title when called with an alert attachment (1 alert)', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ + theCase: mockCase, + attachments: [alertComment as SupportedCaseAttachment], + }); + validateTitle('An alert was added to "Another horrible breach!!'); + }); + + it('should display the alert sync title when called with an alert attachment (multiple alerts)', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + const alert = { + ...alertComment, + alertId: ['1234', '54321'], + } as SupportedCaseAttachment; + + result.current.showSuccessAttach({ + theCase: mockCase, + attachments: [alert], + }); + validateTitle('Alerts were added to "Another horrible breach!!'); + }); + + it('should display a generic title when called with a non-alert attachament', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ + theCase: mockCase, + attachments: [basicComment as SupportedCaseAttachment], + }); + validateTitle('Another horrible breach!! has been updated'); }); - validateTitle('An alert was added to "Another horrible breach!!'); }); - it('should display the alert sync title when called with an alert attachment (multiple alerts)', () => { - const { result } = renderHook( - () => { - return useCasesToast(); - }, - { wrapper: TestProviders } - ); - const alert = { - ...alertComment, - alertId: ['1234', '54321'], - } as SupportedCaseAttachment; - - result.current.showSuccessAttach({ - theCase: mockCase, - attachments: [alert], + describe('Toast content', () => { + let appMockRender: AppMockRenderer; + const onViewCaseClick = jest.fn(); + beforeEach(() => { + appMockRender = createAppMockRenderer(); + onViewCaseClick.mockReset(); + }); + + it('should create a success toast when invoked with a case and a custom content', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ theCase: mockCase, content: 'Custom content' }); + validateContent('Custom content'); + }); + + it('renders an alert-specific content when called with an alert attachment and sync on', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ + theCase: mockCase, + attachments: [alertComment as SupportedCaseAttachment], + }); + validateContent('The alert statuses are synched with the case status.'); + }); + + it('renders empty content when called with an alert attachment and sync off', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + result.current.showSuccessAttach({ + theCase: { ...mockCase, settings: { ...mockCase.settings, syncAlerts: false } }, + attachments: [alertComment as SupportedCaseAttachment], + }); + validateContent('View case'); + }); + + it('renders a correct successful message content', () => { + const result = appMockRender.render( + + ); + expect(result.getByTestId('toaster-content-sync-text')).toHaveTextContent('my content'); + expect(result.getByTestId('toaster-content-case-view-link')).toHaveTextContent('View case'); + expect(onViewCaseClick).not.toHaveBeenCalled(); + }); + + it('renders a correct successful message without content', () => { + const result = appMockRender.render( + + ); + expect(result.queryByTestId('toaster-content-sync-text')).toBeFalsy(); + expect(result.getByTestId('toaster-content-case-view-link')).toHaveTextContent('View case'); + expect(onViewCaseClick).not.toHaveBeenCalled(); + }); + + it('Calls the onViewCaseClick when clicked', () => { + const result = appMockRender.render( + + ); + userEvent.click(result.getByTestId('toaster-content-case-view-link')); + expect(onViewCaseClick).toHaveBeenCalled(); }); - validateTitle('Alerts were added to "Another horrible breach!!'); }); - it('should display a generic title when called with a non-alert attachament', () => { - const { result } = renderHook( - () => { - return useCasesToast(); - }, - { wrapper: TestProviders } - ); - result.current.showSuccessAttach({ - theCase: mockCase, - attachments: [basicComment as SupportedCaseAttachment], + describe('Toast navigation', () => { + const tests = Object.entries(OWNER_INFO).map(([owner, ownerInfo]) => [ + owner, + ownerInfo.appId, + ]); + + it.each(tests)('should navigate correctly with owner %s and appId %s', (owner, appId) => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + + result.current.showSuccessAttach({ + theCase: { ...mockCase, owner }, + title: 'Custom title', + }); + + navigateToCase(); + + expect(getUrlForApp).toHaveBeenCalledWith(appId, { + deepLinkId: 'cases', + path: '/mock-id', + }); + + expect(navigateToUrl).toHaveBeenCalledWith('/app/cases/mock-id'); + }); + + it('navigates to the current app if the owner is invalid', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + + result.current.showSuccessAttach({ + theCase: { ...mockCase, owner: 'in-valid' }, + title: 'Custom title', + }); + + navigateToCase(); + + expect(getUrlForApp).toHaveBeenCalledWith('testAppId', { + deepLinkId: 'cases', + path: '/mock-id', + }); }); - validateTitle('Another horrible breach!! has been updated'); }); }); - describe('Toast content', () => { - let appMockRender: AppMockRenderer; - const onViewCaseClick = jest.fn(); - beforeEach(() => { - appMockRender = createAppMockRenderer(); - onViewCaseClick.mockReset(); - }); + describe('showErrorToast', () => { + it('should show an error toast', () => { + const error = new Error('showErrorToast: an error occurred'); - it('should create a success toast when invoked with a case and a custom content', () => { const { result } = renderHook( () => { return useCasesToast(); }, { wrapper: TestProviders } ); - result.current.showSuccessAttach({ theCase: mockCase, content: 'Custom content' }); - validateContent('Custom content'); + + result.current.showErrorToast(error); + + expect(errorMock).toHaveBeenCalledWith(error, { title: error.message }); }); - it('renders an alert-specific content when called with an alert attachment and sync on', () => { + it('should override the title', () => { + const error = new Error('showErrorToast: an error occurred'); + const { result } = renderHook( () => { return useCasesToast(); }, { wrapper: TestProviders } ); - result.current.showSuccessAttach({ - theCase: mockCase, - attachments: [alertComment as SupportedCaseAttachment], - }); - validateContent('The alert statuses are synched with the case status.'); + + result.current.showErrorToast(error, { title: 'my title' }); + + expect(errorMock).toHaveBeenCalledWith(error, { title: 'my title' }); }); - it('renders empty content when called with an alert attachment and sync off', () => { + it('should not show an error toast if the error is AbortError', () => { + const error = new Error('showErrorToast: an error occurred'); + error.name = 'AbortError'; + const { result } = renderHook( () => { return useCasesToast(); }, { wrapper: TestProviders } ); - result.current.showSuccessAttach({ - theCase: { ...mockCase, settings: { ...mockCase.settings, syncAlerts: false } }, - attachments: [alertComment as SupportedCaseAttachment], - }); - validateContent('View case'); - }); - it('renders a correct successful message content', () => { - const result = appMockRender.render( - - ); - expect(result.getByTestId('toaster-content-sync-text')).toHaveTextContent('my content'); - expect(result.getByTestId('toaster-content-case-view-link')).toHaveTextContent('View case'); - expect(onViewCaseClick).not.toHaveBeenCalled(); - }); - - it('renders a correct successful message without content', () => { - const result = appMockRender.render( - - ); - expect(result.queryByTestId('toaster-content-sync-text')).toBeFalsy(); - expect(result.getByTestId('toaster-content-case-view-link')).toHaveTextContent('View case'); - expect(onViewCaseClick).not.toHaveBeenCalled(); - }); + result.current.showErrorToast(error); - it('Calls the onViewCaseClick when clicked', () => { - const result = appMockRender.render( - - ); - userEvent.click(result.getByTestId('toaster-content-case-view-link')); - expect(onViewCaseClick).toHaveBeenCalled(); + expect(errorMock).not.toHaveBeenCalled(); }); - }); - describe('Toast navigation', () => { - const tests = Object.entries(OWNER_INFO).map(([owner, ownerInfo]) => [owner, ownerInfo.appId]); + it('should show the body message if it is a ServerError', () => { + const error = new Error('showErrorToast: an error occurred'); + // @ts-expect-error: need to create a ServerError + error.body = { message: 'message error' }; - it.each(tests)('should navigate correctly with owner %s and appId %s', (owner, appId) => { const { result } = renderHook( () => { return useCasesToast(); @@ -223,22 +326,16 @@ describe('Use cases toast hook', () => { { wrapper: TestProviders } ); - result.current.showSuccessAttach({ - theCase: { ...mockCase, owner }, - title: 'Custom title', - }); + result.current.showErrorToast(error); - navigateToCase(); - - expect(getUrlForApp).toHaveBeenCalledWith(appId, { - deepLinkId: 'cases', - path: '/mock-id', + expect(errorMock).toHaveBeenCalledWith(new Error('message error'), { + title: 'message error', }); - - expect(navigateToUrl).toHaveBeenCalledWith('/app/cases/mock-id'); }); + }); - it('navigates to the current app if the owner is invalid', () => { + describe('showSuccessToast', () => { + it('should show a success toast', () => { const { result } = renderHook( () => { return useCasesToast(); @@ -246,17 +343,9 @@ describe('Use cases toast hook', () => { { wrapper: TestProviders } ); - result.current.showSuccessAttach({ - theCase: { ...mockCase, owner: 'in-valid' }, - title: 'Custom title', - }); - - navigateToCase(); + result.current.showSuccessToast('my title'); - expect(getUrlForApp).toHaveBeenCalledWith('testAppId', { - deepLinkId: 'cases', - path: '/mock-id', - }); + expect(successMock).toHaveBeenCalledWith('my title'); }); }); }); diff --git a/x-pack/plugins/cases/public/common/use_cases_toast.tsx b/x-pack/plugins/cases/public/common/use_cases_toast.tsx index 7d445a4edffa..42a358897bc6 100644 --- a/x-pack/plugins/cases/public/common/use_cases_toast.tsx +++ b/x-pack/plugins/cases/public/common/use_cases_toast.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { ErrorToastOptions } from '@kbn/core/public'; import { EuiButtonEmpty, EuiText } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; @@ -12,7 +13,7 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { Case, CommentType } from '../../common'; import { useKibana, useToasts } from './lib/kibana'; import { generateCaseViewPath } from './navigation'; -import { CaseAttachmentsWithoutOwner } from '../types'; +import { CaseAttachmentsWithoutOwner, ServerError } from '../types'; import { CASE_ALERT_SUCCESS_SYNC_TEXT, CASE_ALERT_SUCCESS_TOAST, @@ -98,6 +99,25 @@ function getToastContent({ const isValidOwner = (owner: string): owner is keyof typeof OWNER_INFO => Object.keys(OWNER_INFO).includes(owner); +const isServerError = (error: Error | ServerError): error is ServerError => + Object.hasOwn(error, 'body'); + +const getError = (error: Error | ServerError): Error => { + if (isServerError(error)) { + return new Error(error.body?.message); + } + + return error; +}; + +const getErrorMessage = (error: Error | ServerError): string => { + if (isServerError(error)) { + return error.body?.message ?? ''; + } + + return error.message; +}; + export const useCasesToast = () => { const { appId } = useCasesContext(); const { getUrlForApp, navigateToUrl } = useKibana().services.application; @@ -141,6 +161,14 @@ export const useCasesToast = () => { ), }); }, + showErrorToast: (error: Error | ServerError, opts?: ErrorToastOptions) => { + if (error.name !== 'AbortError') { + toasts.addError(getError(error), { title: getErrorMessage(error), ...opts }); + } + }, + showSuccessToast: (title: string) => { + toasts.addSuccess(title); + }, }; }; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index a504bf251ff2..1cc3a5f0263e 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; import moment from 'moment-timezone'; -import { act, render, waitFor, screen } from '@testing-library/react'; +import { render, waitFor, screen, act } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; @@ -20,15 +20,12 @@ import { noDeleteCasesPermissions, TestProviders, } from '../../common/mock'; -import { casesStatus, useGetCasesMockState, mockCase, connectorsMock } from '../../containers/mock'; +import { useGetCasesMockState, connectorsMock } from '../../containers/mock'; import { StatusAll } from '../../../common/ui/types'; import { CaseSeverity, CaseStatuses } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; -import { useDeleteCases } from '../../containers/use_delete_cases'; -import { useGetCasesStatus } from '../../containers/use_get_cases_status'; -import { useUpdateCases } from '../../containers/use_bulk_update_case'; import { useKibana } from '../../common/lib/kibana'; import { AllCasesList } from './all_cases_list'; import { CasesColumns, GetCasesColumn, useCasesColumns } from './columns'; @@ -37,7 +34,6 @@ import { registerConnectorsToMockActionRegistry } from '../../common/mock/regist import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; import { waitForComponentToUpdate } from '../../common/test_utils'; import { useCreateAttachments } from '../../containers/use_create_attachments'; -import { useGetCasesMetrics } from '../../containers/use_get_cases_metrics'; import { useGetConnectors } from '../../containers/configure/use_connectors'; import { useGetTags } from '../../containers/use_get_tags'; import { useUpdateCase } from '../../containers/use_update_case'; @@ -46,13 +42,10 @@ import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get import { userProfiles, userProfilesMap } from '../../containers/user_profiles/api.mock'; import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles'; import { useLicense } from '../../common/use_license'; +import * as api from '../../containers/api'; jest.mock('../../containers/use_create_attachments'); -jest.mock('../../containers/use_bulk_update_case'); -jest.mock('../../containers/use_delete_cases'); jest.mock('../../containers/use_get_cases'); -jest.mock('../../containers/use_get_cases_status'); -jest.mock('../../containers/use_get_cases_metrics'); jest.mock('../../containers/use_get_action_license'); jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/user_profiles/use_get_current_user_profile'); @@ -66,11 +59,7 @@ jest.mock('../app/use_available_owners', () => ({ jest.mock('../../containers/use_update_case'); jest.mock('../../common/use_license'); -const useDeleteCasesMock = useDeleteCases as jest.Mock; const useGetCasesMock = useGetCases as jest.Mock; -const useGetCasesStatusMock = useGetCasesStatus as jest.Mock; -const useGetCasesMetricsMock = useGetCasesMetrics as jest.Mock; -const useUpdateCasesMock = useUpdateCases as jest.Mock; const useGetTagsMock = useGetTags as jest.Mock; const useGetCurrentUserProfileMock = useGetCurrentUserProfile as jest.Mock; const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock; @@ -92,13 +81,7 @@ const mockKibana = () => { }; describe('AllCasesListGeneric', () => { - const dispatchResetIsDeleted = jest.fn(); - const dispatchResetIsUpdated = jest.fn(); - const handleOnDeleteConfirm = jest.fn(); - const handleToggleModal = jest.fn(); const refetchCases = jest.fn(); - const updateBulkStatus = jest.fn(); - const fetchCasesStatus = jest.fn(); const onRowClick = jest.fn(); const updateCaseProperty = jest.fn(); @@ -113,36 +96,6 @@ describe('AllCasesListGeneric', () => { refetch: refetchCases, }; - const defaultDeleteCases = { - dispatchResetIsDeleted, - handleOnDeleteConfirm, - handleToggleModal, - isDeleted: false, - isDisplayConfirmDeleteModal: false, - isLoading: false, - }; - - const defaultCasesStatus = { - ...casesStatus, - fetchCasesStatus, - isError: false, - isLoading: false, - }; - - const defaultCasesMetrics = { - mttr: 5, - isLoading: false, - fetchCasesMetrics: jest.fn(), - }; - - const defaultUpdateCases = { - isUpdated: false, - isLoading: false, - isError: false, - dispatchResetIsUpdated, - updateBulkStatus, - }; - const defaultColumnArgs = { caseDetailsNavigation: { href: jest.fn(), @@ -167,11 +120,7 @@ describe('AllCasesListGeneric', () => { beforeEach(() => { jest.clearAllMocks(); appMockRenderer = createAppMockRenderer(); - useUpdateCasesMock.mockReturnValue(defaultUpdateCases); useGetCasesMock.mockReturnValue(defaultGetCases); - useDeleteCasesMock.mockReturnValue(defaultDeleteCases); - useGetCasesStatusMock.mockReturnValue(defaultCasesStatus); - useGetCasesMetricsMock.mockReturnValue(defaultCasesMetrics); useGetTagsMock.mockReturnValue({ data: ['coke', 'pepsi'], refetch: jest.fn() }); useGetCurrentUserProfileMock.mockReturnValue({ data: userProfiles[0], isLoading: false }); useBulkGetUserProfilesMock.mockReturnValue({ data: userProfilesMap }); @@ -368,43 +317,48 @@ describe('AllCasesListGeneric', () => { expect(wrapper.find('[data-test-subj="cases-count-stats"]')).toBeTruthy(); }); - it.skip('Bulk delete', async () => { - useDeleteCasesMock - .mockReturnValueOnce({ - ...defaultDeleteCases, - isDisplayConfirmDeleteModal: false, - }) - .mockReturnValue({ - ...defaultDeleteCases, - isDisplayConfirmDeleteModal: true, - }); + it('Bulk delete', async () => { + const deleteCasesSpy = jest.spyOn(api, 'deleteCases'); + const result = appMockRenderer.render(); - const wrapper = mount( - - - - ); + act(() => { + userEvent.click(result.getByTestId('checkboxSelectAll')); + }); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().simulate('click'); + act(() => { + userEvent.click(result.getByText('Bulk actions')); + }); - wrapper - .find( - '[data-test-subj="confirm-delete-case-modal"] [data-test-subj="confirmModalConfirmButton"]' - ) - .last() - .simulate('click'); + await waitForEuiPopoverOpen(); + + act(() => { + userEvent.click(result.getByTestId('cases-bulk-delete-button'), undefined, { + skipPointerEventsCheck: true, + }); + }); await waitFor(() => { - expect(handleToggleModal).toBeCalled(); + expect(result.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + }); - expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual([ - ...useGetCasesMockState.data.cases.map(({ id, title }) => ({ id, title })), - { - id: mockCase.id, - title: mockCase.title, - }, - ]); + act(() => { + userEvent.click(result.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(deleteCasesSpy).toHaveBeenCalledWith( + [ + 'basic-case-id', + '1', + '2', + '3', + '4', + 'case-with-alerts-id', + 'case-with-alerts-syncoff-id', + 'case-with-registered-attachment', + ], + expect.anything() + ); }); }); @@ -431,6 +385,8 @@ describe('AllCasesListGeneric', () => { }); it('Bulk close status update', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + const result = appMockRenderer.render(); const theCase = useGetCasesMockState.data.cases[0]; userEvent.click(result.getByTestId('case-status-filter')); @@ -441,10 +397,16 @@ describe('AllCasesListGeneric', () => { await waitForEuiPopoverOpen(); userEvent.click(result.getByTestId('cases-bulk-close-button')); await waitFor(() => {}); - expect(updateBulkStatus).toBeCalledWith([theCase], CaseStatuses.closed); + + expect(updateCasesSpy).toBeCalledWith( + [{ id: theCase.id, version: theCase.version, status: CaseStatuses.closed }], + expect.anything() + ); }); it('Bulk open status update', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + const result = appMockRenderer.render(); const theCase = useGetCasesMockState.data.cases[0]; userEvent.click(result.getByTestId('case-status-filter')); @@ -455,10 +417,16 @@ describe('AllCasesListGeneric', () => { await waitForEuiPopoverOpen(); userEvent.click(result.getByTestId('cases-bulk-open-button')); await waitFor(() => {}); - expect(updateBulkStatus).toBeCalledWith([theCase], CaseStatuses.open); + + expect(updateCasesSpy).toBeCalledWith( + [{ id: theCase.id, version: theCase.version, status: CaseStatuses.open }], + expect.anything() + ); }); it('Bulk in-progress status update', async () => { + const updateCasesSpy = jest.spyOn(api, 'updateCases'); + const result = appMockRenderer.render(); const theCase = useGetCasesMockState.data.cases[0]; userEvent.click(result.getByTestId('case-status-filter')); @@ -469,43 +437,11 @@ describe('AllCasesListGeneric', () => { await waitForEuiPopoverOpen(); userEvent.click(result.getByTestId('cases-bulk-in-progress-button')); await waitFor(() => {}); - expect(updateBulkStatus).toBeCalledWith([theCase], CaseStatuses['in-progress']); - }); - it('isDeleted is true, refetch', async () => { - useDeleteCasesMock.mockReturnValue({ - ...defaultDeleteCases, - isDeleted: true, - }); - - mount( - - - + expect(updateCasesSpy).toBeCalledWith( + [{ id: theCase.id, version: theCase.version, status: CaseStatuses['in-progress'] }], + expect.anything() ); - await waitFor(() => { - expect(refetchCases).toBeCalled(); - // expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsDeleted).toBeCalled(); - }); - }); - - it('isUpdated is true, refetch', async () => { - useUpdateCasesMock.mockReturnValue({ - ...defaultUpdateCases, - isUpdated: true, - }); - - mount( - - - - ); - await waitFor(() => { - expect(refetchCases).toBeCalled(); - // expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsUpdated).toBeCalled(); - }); }); it('should not render table utility bar when isSelectorView=true', async () => { @@ -769,28 +705,10 @@ describe('AllCasesListGeneric', () => { expect(wrapper.find('[data-test-subj="status-badge-in-progress"]').exists()).toBeTruthy(); }); - it('should call doRefresh if provided', async () => { - const doRefresh = jest.fn(); - - const wrapper = mount( - - - - ); - - await act(async () => { - wrapper.find('[data-test-subj="all-cases-refresh"] button').first().simulate('click'); - }); - - expect(doRefresh).toHaveBeenCalled(); - }); - it('shows Solution column if there are no set owners', async () => { - const doRefresh = jest.fn(); - const wrapper = mount( - + ); @@ -801,11 +719,9 @@ describe('AllCasesListGeneric', () => { }); it('hides Solution column if there is a set owner', async () => { - const doRefresh = jest.fn(); - const wrapper = mount( - + ); diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index 0bc5e9b14b29..c7b2d4895f94 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -10,7 +10,6 @@ import { EuiProgress, EuiBasicTable, EuiTableSelectionType } from '@elastic/eui' import { difference, head, isEmpty } from 'lodash/fp'; import styled, { css } from 'styled-components'; -import { useQueryClient } from '@tanstack/react-query'; import { Case, CaseStatusWithAllStatus, @@ -38,11 +37,8 @@ import { } from '../../containers/use_get_cases'; import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles'; import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile'; -import { - USER_PROFILES_BULK_GET_CACHE_KEY, - USER_PROFILES_CACHE_KEY, -} from '../../containers/constants'; import { getAllPermissionsExceptFrom } from '../../utils/permissions'; +import { useIsLoadingCases } from './use_is_loading_cases'; const ProgressLoader = styled(EuiProgress)` ${({ $isShow }: { $isShow: boolean }) => @@ -64,14 +60,13 @@ export interface AllCasesListProps { hiddenStatuses?: CaseStatusWithAllStatus[]; isSelectorView?: boolean; onRowClick?: (theCase?: Case) => void; - doRefresh?: () => void; } export const AllCasesList = React.memo( - ({ hiddenStatuses = [], isSelectorView = false, onRowClick, doRefresh }) => { + ({ hiddenStatuses = [], isSelectorView = false, onRowClick }) => { const { owner, permissions } = useCasesContext(); const availableSolutions = useAvailableCasesOwners(getAllPermissionsExceptFrom('delete')); - const [refresh, setRefresh] = useState(0); + const isLoading = useIsLoadingCases(); const hasOwner = !!owner.length; @@ -80,19 +75,15 @@ export const AllCasesList = React.memo( ...(!isEmpty(hiddenStatuses) && firstAvailableStatus && { status: firstAvailableStatus }), owner: hasOwner ? owner : availableSolutions, }; + const [filterOptions, setFilterOptions] = useState({ ...DEFAULT_FILTER_OPTIONS, ...initialFilterOptions, }); const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_PARAMS); const [selectedCases, setSelectedCases] = useState([]); - const queryClient = useQueryClient(); - const { - data = initialData, - isFetching: isLoadingCases, - refetch: refetchCases, - } = useGetCases({ + const { data = initialData, isFetching: isLoadingCases } = useGetCases({ filterOptions, queryParams, }); @@ -126,41 +117,13 @@ export const AllCasesList = React.memo( [queryParams.sortField, queryParams.sortOrder] ); - const filterRefetch = useRef<() => void>(); const tableRef = useRef(null); - const [isLoading, handleIsLoading] = useState(false); - - const setFilterRefetch = useCallback( - (refetchFilter: () => void) => { - filterRefetch.current = refetchFilter; - }, - [filterRefetch] - ); const deselectCases = useCallback(() => { setSelectedCases([]); tableRef.current?.setSelection([]); }, [setSelectedCases]); - const refreshCases = useCallback( - (dataRefresh = true) => { - deselectCases(); - if (dataRefresh) { - refetchCases(); - queryClient.refetchQueries([USER_PROFILES_CACHE_KEY, USER_PROFILES_BULK_GET_CACHE_KEY]); - - setRefresh((currRefresh: number) => currRefresh + 1); - } - if (doRefresh) { - doRefresh(); - } - if (filterRefetch.current != null) { - filterRefetch.current(); - } - }, - [deselectCases, doRefresh, queryClient, refetchCases] - ); - const tableOnChangeCallback = useCallback( ({ page, sort }: EuiBasicTableOnChange) => { let newQueryParams = queryParams; @@ -179,9 +142,9 @@ export const AllCasesList = React.memo( }; } setQueryParams(newQueryParams); - refreshCases(false); + deselectCases(); }, - [queryParams, refreshCases, setQueryParams] + [queryParams, deselectCases, setQueryParams] ); const onFilterChangedCallback = useCallback( @@ -229,9 +192,8 @@ export const AllCasesList = React.memo( } : {}), })); - refreshCases(false); }, - [deselectCases, refreshCases, hasOwner, availableSolutions, owner] + [deselectCases, hasOwner, availableSolutions, owner] ); /** @@ -244,8 +206,6 @@ export const AllCasesList = React.memo( filterStatus: filterOptions.status ?? StatusAll, userProfiles: userProfiles ?? new Map(), currentUserProfile, - handleIsLoading, - refreshCases, isSelectorView, connectors, onRowClick, @@ -286,7 +246,7 @@ export const AllCasesList = React.memo( className="essentialAnimation" $isShow={isLoading || isLoadingCases} /> - {!isSelectorView ? : null} + {!isSelectorView ? : null} ( owner: filterOptions.owner, severity: filterOptions.severity, }} - setFilterRefetch={setFilterRefetch} hiddenStatuses={hiddenStatuses} displayCreateCaseButton={isSelectorView} onCreateCasePressed={onRowClick} @@ -315,20 +274,19 @@ export const AllCasesList = React.memo( data={data} filterOptions={filterOptions} goToCreateCase={onRowClick} - handleIsLoading={handleIsLoading} isCasesLoading={isLoadingCases} isCommentUpdating={isLoadingCases} isDataEmpty={isDataEmpty} isSelectorView={isSelectorView} onChange={tableOnChangeCallback} pagination={pagination} - refreshCases={refreshCases} selectedCases={selectedCases} selection={euiBasicTableSelectionProps} showActions={showActions} sorting={sorting} tableRef={tableRef} tableRowProps={tableRowProps} + deselectCases={deselectCases} /> ); diff --git a/x-pack/plugins/cases/public/components/all_cases/cases_metrics.test.tsx b/x-pack/plugins/cases/public/components/all_cases/cases_metrics.test.tsx index 6141527d59f4..323f9deead3e 100644 --- a/x-pack/plugins/cases/public/components/all_cases/cases_metrics.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/cases_metrics.test.tsx @@ -5,46 +5,29 @@ * 2.0. */ -import { within } from '@testing-library/dom'; +import { waitFor, within } from '@testing-library/react'; import React from 'react'; import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; -import { useGetCasesMetrics } from '../../containers/use_get_cases_metrics'; -import { useGetCasesStatus } from '../../containers/use_get_cases_status'; import { CasesMetrics } from './cases_metrics'; -jest.mock('../../containers/use_get_cases_metrics'); -jest.mock('../../containers/use_get_cases_status'); - -const useGetCasesMetricsMock = useGetCasesMetrics as jest.Mock; -const useGetCasesStatusMock = useGetCasesStatus as jest.Mock; +jest.mock('../../api'); describe('Cases metrics', () => { - useGetCasesStatusMock.mockReturnValue({ - countOpenCases: 2, - countInProgressCases: 3, - countClosedCases: 4, - isLoading: false, - fetchCasesStatus: jest.fn(), - }); - useGetCasesMetricsMock.mockReturnValue({ - // 600 seconds = 10m - mttr: 600, - isLoading: false, - fetchCasesMetrics: jest.fn(), - }); - let appMockRenderer: AppMockRenderer; beforeEach(() => { appMockRenderer = createAppMockRenderer(); }); - it('renders the correct stats', () => { - const result = appMockRenderer.render(); - expect(result.getByTestId('cases-metrics-stats')).toBeTruthy(); - expect(within(result.getByTestId('openStatsHeader')).getByText(2)).toBeTruthy(); - expect(within(result.getByTestId('inProgressStatsHeader')).getByText(3)).toBeTruthy(); - expect(within(result.getByTestId('closedStatsHeader')).getByText(4)).toBeTruthy(); - expect(within(result.getByTestId('mttrStatsHeader')).getByText('10m')).toBeTruthy(); + it('renders the correct stats', async () => { + const result = appMockRenderer.render(); + + await waitFor(() => { + expect(result.getByTestId('cases-metrics-stats')).toBeTruthy(); + expect(within(result.getByTestId('openStatsHeader')).getByText(20)).toBeTruthy(); + expect(within(result.getByTestId('inProgressStatsHeader')).getByText(40)).toBeTruthy(); + expect(within(result.getByTestId('closedStatsHeader')).getByText(130)).toBeTruthy(); + expect(within(result.getByTestId('mttrStatsHeader')).getByText('12s')).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/cases_metrics.tsx b/x-pack/plugins/cases/public/components/all_cases/cases_metrics.tsx index e75f0fd8a670..ca6434aa11c2 100644 --- a/x-pack/plugins/cases/public/components/all_cases/cases_metrics.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/cases_metrics.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FunctionComponent, useEffect, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { EuiDescriptionList, EuiFlexGroup, @@ -22,9 +22,6 @@ import { StatusStats } from '../status/status_stats'; import { useGetCasesMetrics } from '../../containers/use_get_cases_metrics'; import { ATTC_DESCRIPTION, ATTC_STAT } from './translations'; -interface CountProps { - refresh?: number; -} const MetricsFlexGroup = styled.div` ${({ theme }) => css` .euiFlexGroup { @@ -40,29 +37,23 @@ const MetricsFlexGroup = styled.div` `} `; -export const CasesMetrics: FunctionComponent = ({ refresh }) => { +export const CasesMetrics: React.FC = () => { const { - countOpenCases, - countInProgressCases, - countClosedCases, + data: { countOpenCases, countInProgressCases, countClosedCases } = { + countOpenCases: 0, + countInProgressCases: 0, + countClosedCases: 0, + }, isLoading: isCasesStatusLoading, - fetchCasesStatus, } = useGetCasesStatus(); - const { mttr, isLoading: isCasesMetricsLoading, fetchCasesMetrics } = useGetCasesMetrics(); + const { data: { mttr } = { mttr: 0 }, isLoading: isCasesMetricsLoading } = useGetCasesMetrics(); const mttrValue = useMemo( () => (mttr != null ? prettyMilliseconds(mttr * 1000, { compact: true, verbose: false }) : '-'), [mttr] ); - useEffect(() => { - if (refresh != null) { - fetchCasesStatus(); - fetchCasesMetrics(); - } - }, [fetchCasesMetrics, fetchCasesStatus, refresh]); - return ( diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index 00e9fb077589..948abdbbcd2f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiBadgeGroup, EuiBadge, @@ -24,7 +24,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { UserProfileWithAvatar } from '@kbn/user-profile-components'; -import { Case, DeleteCase, UpdateByKey } from '../../../common/ui/types'; +import { Case, UpdateByKey } from '../../../common/ui/types'; import { CaseStatuses, ActionConnector, CaseSeverity } from '../../../common/api'; import { OWNER_INFO } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; @@ -49,6 +49,7 @@ import { getUsernameDataTestSubj } from '../user_profiles/data_test_subject'; import { CurrentUserProfile } from '../types'; import { SmallUserAvatar } from '../user_profiles/small_user_avatar'; import { useCasesFeatures } from '../../common/use_cases_features'; +import { useRefreshCases } from './use_on_refresh_cases'; export type CasesColumns = | EuiTableActionsColumnType @@ -103,8 +104,6 @@ export interface GetCasesColumn { filterStatus: string; userProfiles: Map; currentUserProfile: CurrentUserProfile; - handleIsLoading: (a: boolean) => void; - refreshCases?: (a?: boolean) => void; isSelectorView: boolean; connectors?: ActionConnector[]; onRowClick?: (theCase: Case) => void; @@ -115,41 +114,40 @@ export const useCasesColumns = ({ filterStatus, userProfiles, currentUserProfile, - handleIsLoading, - refreshCases, isSelectorView, connectors = [], onRowClick, showSolutionColumn, }: GetCasesColumn): CasesColumns[] => { - // Delete case - const { - dispatchResetIsDeleted, - handleOnDeleteConfirm, - handleToggleModal, - isDeleted, - isDisplayConfirmDeleteModal, - isLoading: isDeleting, - } = useDeleteCases(); - + const [isModalVisible, setIsModalVisible] = useState(false); + const { mutate: deleteCases } = useDeleteCases(); + const refreshCases = useRefreshCases(); const { isAlertsEnabled, caseAssignmentAuthorized } = useCasesFeatures(); const { permissions } = useCasesContext(); - - const [deleteThisCase, setDeleteThisCase] = useState({ - id: '', - title: '', - }); - + const [caseToBeDeleted, setCaseToBeDeleted] = useState(); const { updateCaseProperty, isLoading: isLoadingUpdateCase } = useUpdateCase(); - const toggleDeleteModal = useCallback( - (deleteCase: Case) => { - handleToggleModal(); - setDeleteThisCase({ id: deleteCase.id, title: deleteCase.title }); + const closeModal = useCallback(() => setIsModalVisible(false), []); + const openModal = useCallback(() => setIsModalVisible(true), []); + + const onDeleteAction = useCallback( + (theCase: Case) => { + openModal(); + setCaseToBeDeleted(theCase.id); }, - [handleToggleModal] + [openModal] ); + const onConfirmDeletion = useCallback(() => { + closeModal(); + if (caseToBeDeleted) { + deleteCases({ + caseIds: [caseToBeDeleted], + successToasterTitle: i18n.DELETED_CASES(1), + }); + } + }, [caseToBeDeleted, closeModal, deleteCases]); + const handleDispatchUpdate = useCallback( ({ updateKey, updateValue, caseData }: UpdateByKey) => { updateCaseProperty({ @@ -157,7 +155,7 @@ export const useCasesColumns = ({ updateValue, caseData, onSuccess: () => { - if (refreshCases != null) refreshCases(); + refreshCases(); }, }); }, @@ -167,9 +165,9 @@ export const useCasesColumns = ({ const actions = useMemo( () => getActions({ - deleteCaseOnClick: toggleDeleteModal, + deleteCaseOnClick: onDeleteAction, }), - [toggleDeleteModal] + [onDeleteAction] ); const assignCaseAction = useCallback( @@ -181,17 +179,6 @@ export const useCasesColumns = ({ [onRowClick] ); - useEffect(() => { - handleIsLoading(isDeleting || isLoadingUpdateCase); - }, [handleIsLoading, isDeleting, isLoadingUpdateCase]); - - useEffect(() => { - if (isDeleted) { - if (refreshCases != null) refreshCases(); - dispatchResetIsDeleted(); - } - }, [isDeleted, dispatchResetIsDeleted, refreshCases]); - return [ { name: i18n.NAME, @@ -421,12 +408,13 @@ export const useCasesColumns = ({ name: ( <> {i18n.ACTIONS} - + {isModalVisible ? ( + + ) : null} ), actions, diff --git a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx index 6fd1556f7d4e..55777dd5bd34 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx @@ -15,8 +15,7 @@ import { noCreateCasesPermissions, } from '../../common/mock'; import { useGetActionLicense } from '../../containers/use_get_action_license'; -import { casesStatus, connectorsMock, useGetCasesMockState } from '../../containers/mock'; -import { useGetCasesStatus } from '../../containers/use_get_cases_status'; +import { connectorsMock, useGetCasesMockState } from '../../containers/mock'; import { useGetConnectors } from '../../containers/configure/use_connectors'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetCases } from '../../containers/use_get_cases'; @@ -33,13 +32,12 @@ jest.mock('../../containers/use_get_action_license', () => { jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/api'); jest.mock('../../containers/use_get_cases'); -jest.mock('../../containers/use_get_cases_status'); jest.mock('../../containers/user_profiles/use_get_current_user_profile'); jest.mock('../../containers/user_profiles/use_bulk_get_user_profiles'); +jest.mock('../../api'); const useGetConnectorsMock = useGetConnectors as jest.Mock; const useGetCasesMock = useGetCases as jest.Mock; -const useGetCasesStatusMock = useGetCasesStatus as jest.Mock; const useGetActionLicenseMock = useGetActionLicense as jest.Mock; const useGetCurrentUserProfileMock = useGetCurrentUserProfile as jest.Mock; const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock; @@ -49,7 +47,6 @@ describe('AllCases', () => { const setFilters = jest.fn(); const setQueryParams = jest.fn(); const setSelectedCases = jest.fn(); - const fetchCasesStatus = jest.fn(); const defaultGetCases = { ...useGetCasesMockState, @@ -59,13 +56,6 @@ describe('AllCases', () => { setSelectedCases, }; - const defaultCasesStatus = { - ...casesStatus, - fetchCasesStatus, - isError: false, - isLoading: false, - }; - const defaultActionLicense = { data: null, isLoading: false, @@ -75,7 +65,6 @@ describe('AllCases', () => { beforeAll(() => { (useGetTags as jest.Mock).mockReturnValue({ data: ['coke', 'pepsi'], refetch: jest.fn() }); useGetConnectorsMock.mockImplementation(() => ({ data: connectorsMock, isLoading: false })); - useGetCasesStatusMock.mockReturnValue(defaultCasesStatus); useGetActionLicenseMock.mockReturnValue(defaultActionLicense); useGetCasesMock.mockReturnValue(defaultGetCases); @@ -142,8 +131,6 @@ describe('AllCases', () => { }); it('should render the loading spinner when loading stats', async () => { - useGetCasesStatusMock.mockReturnValue({ ...defaultCasesStatus, isLoading: true }); - const result = appMockRender.render(); await waitFor(() => { diff --git a/x-pack/plugins/cases/public/components/all_cases/table.tsx b/x-pack/plugins/cases/public/components/all_cases/table.tsx index afe653a50e4d..208c26e47648 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table.tsx @@ -29,20 +29,19 @@ interface CasesTableProps { data: Cases; filterOptions: FilterOptions; goToCreateCase?: () => void; - handleIsLoading: (a: boolean) => void; isCasesLoading: boolean; isCommentUpdating: boolean; isDataEmpty: boolean; isSelectorView?: boolean; onChange: EuiBasicTableProps['onChange']; pagination: Pagination; - refreshCases: (a?: boolean) => void; selectedCases: Case[]; selection: EuiTableSelectionType; showActions: boolean; sorting: EuiBasicTableProps['sorting']; tableRef: MutableRefObject; tableRowProps: EuiBasicTableProps['rowProps']; + deselectCases: () => void; } const Div = styled.div` @@ -54,20 +53,19 @@ export const CasesTable: FunctionComponent = ({ data, filterOptions, goToCreateCase, - handleIsLoading, isCasesLoading, isCommentUpdating, isDataEmpty, isSelectorView, onChange, pagination, - refreshCases, selectedCases, selection, showActions, sorting, tableRef, tableRowProps, + deselectCases, }) => { const { permissions } = useCasesContext(); const { getCreateCaseUrl, navigateToCreateCase } = useCreateCaseNavigation(); @@ -93,9 +91,8 @@ export const CasesTable: FunctionComponent = ({ data={data} enableBulkActions={showActions} filterOptions={filterOptions} - handleIsLoading={handleIsLoading} selectedCases={selectedCases} - refreshCases={refreshCases} + deselectCases={deselectCases} /> { expect(onFilterChanged).toBeCalledWith({ status: CaseStatuses.closed }); }); - it('should call on load setFilterRefetch', () => { - mount( - - - - ); - expect(setFilterRefetch).toHaveBeenCalled(); - }); - it('should remove tag from selected tags when tag no longer exists', () => { const ourProps = { ...props, diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx index 5ab587ad3175..75fc22a9fee2 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx @@ -19,7 +19,6 @@ import { StatusFilter } from './status_filter'; import * as i18n from './translations'; import { SeverityFilter } from './severity_filter'; import { useGetTags } from '../../containers/use_get_tags'; -import { CASE_LIST_CACHE_KEY } from '../../containers/constants'; import { DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; import { AssigneesFilterPopover } from './assignees_filter'; import { CurrentUserProfile } from '../types'; @@ -31,7 +30,6 @@ interface CasesTableFiltersProps { countOpenCases: number | null; onFilterChanged: (filterOptions: Partial) => void; initial: FilterOptions; - setFilterRefetch: (val: () => void) => void; hiddenStatuses?: CaseStatusWithAllStatus[]; availableSolutions: string[]; displayCreateCaseButton?: boolean; @@ -59,7 +57,6 @@ const CasesTableFiltersComponent = ({ countInProgressCases, onFilterChanged, initial = DEFAULT_FILTER_OPTIONS, - setFilterRefetch, hiddenStatuses, availableSolutions, displayCreateCaseButton, @@ -71,19 +68,9 @@ const CasesTableFiltersComponent = ({ const [selectedTags, setSelectedTags] = useState(initial.tags); const [selectedOwner, setSelectedOwner] = useState([]); const [selectedAssignees, setSelectedAssignees] = useState([]); - const { data: tags = [], refetch: fetchTags } = useGetTags(CASE_LIST_CACHE_KEY); + const { data: tags = [] } = useGetTags(); const { caseAssignmentAuthorized } = useCasesFeatures(); - const refetch = useCallback(() => { - fetchTags(); - }, [fetchTags]); - - useEffect(() => { - if (setFilterRefetch != null) { - setFilterRefetch(refetch); - } - }, [refetch, setFilterRefetch]); - const handleSelectedAssignees = useCallback( (newAssignees: UserProfileWithAvatar[]) => { if (!isEqual(newAssignees, selectedAssignees)) { diff --git a/x-pack/plugins/cases/public/components/all_cases/translations.ts b/x-pack/plugins/cases/public/components/all_cases/translations.ts index 0c4fe02993ea..96a683aee507 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -71,10 +71,6 @@ export const CLOSED = i18n.translate('xpack.cases.caseTable.closed', { defaultMessage: 'Closed', }); -export const DELETE = i18n.translate('xpack.cases.caseTable.delete', { - defaultMessage: 'Delete', -}); - export const SELECT = i18n.translate('xpack.cases.caseTable.select', { defaultMessage: 'Select', }); @@ -134,3 +130,40 @@ export const TOTAL_ASSIGNEES_FILTERED = (total: number) => defaultMessage: '{total, plural, one {# assignee} other {# assignees}} filtered', values: { total }, }); + +export const CLOSED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.containers.closedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const REOPENED_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.containers.reopenedCases', { + values: { caseTitle, totalCases }, + defaultMessage: 'Opened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', + }); + +export const MARK_IN_PROGRESS_CASES = ({ + totalCases, + caseTitle, +}: { + totalCases: number; + caseTitle?: string; +}) => + i18n.translate('xpack.cases.containers.markInProgressCases', { + values: { caseTitle, totalCases }, + defaultMessage: + 'Marked {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}} as in progress', + }); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_is_loading_cases.tsx b/x-pack/plugins/cases/public/components/all_cases/use_is_loading_cases.tsx new file mode 100644 index 000000000000..70f46464ed2f --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_is_loading_cases.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useIsMutating } from '@tanstack/react-query'; +import { casesMutationsKeys } from '../../containers/constants'; + +/** + * Returns true or false if any of the queries and mutations + * are fetching in the all cases page + */ + +export const useIsLoadingCases = (): boolean => { + const isDeletingCases = useIsMutating(casesMutationsKeys.deleteCases); + const isUpdatingCases = useIsMutating(casesMutationsKeys.updateCases); + return Boolean(isDeletingCases) || Boolean(isUpdatingCases); +}; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.test.tsx new file mode 100644 index 000000000000..cd077004b5cf --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.test.tsx @@ -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 { act, renderHook } from '@testing-library/react-hooks'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import { casesQueriesKeys } from '../../containers/constants'; +import { useRefreshCases } from './use_on_refresh_cases'; + +describe('useRefreshCases', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('should refresh data on refresh', async () => { + const queryClientSpy = jest.spyOn(appMockRender.queryClient, 'invalidateQueries'); + + const { result } = renderHook(() => useRefreshCases(), { + wrapper: appMockRender.AppWrapper, + }); + + act(() => { + result.current(); + }); + + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.casesList()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.userProfiles()); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.tsx b/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.tsx new file mode 100644 index 000000000000..b50bed282cce --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/use_on_refresh_cases.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; +import { casesQueriesKeys } from '../../containers/constants'; + +/** + * Using react-query queryClient to invalidate all the + * cases table page cache namespace. + * + * This effectively clears the cache for all the cases table pages and + * forces the page to fetch all the data again. Including + * all cases, user profiles, statuses, metrics, tags, etc. + */ + +export const useRefreshCases = () => { + const queryClient = useQueryClient(); + return useCallback(() => { + queryClient.invalidateQueries(casesQueriesKeys.casesList()); + queryClient.invalidateQueries(casesQueriesKeys.tags()); + queryClient.invalidateQueries(casesQueriesKeys.userProfiles()); + }, [queryClient]); +}; diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index a6247cedd3fa..fdfcdc17d472 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import React, { FunctionComponent, useCallback, useEffect, useState } from 'react'; +import React, { FunctionComponent, useCallback, useState } from 'react'; import { EuiContextMenuPanel } from '@elastic/eui'; +import { CaseStatuses } from '../../../common'; import { UtilityBar, UtilityBarAction, @@ -15,88 +16,70 @@ import { UtilityBarText, } from '../utility_bar'; import * as i18n from './translations'; -import { Cases, Case, DeleteCase, FilterOptions } from '../../../common/ui/types'; +import { Cases, Case, FilterOptions } from '../../../common/ui/types'; import { getBulkItems } from '../bulk_actions'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; import { useUpdateCases } from '../../containers/use_bulk_update_case'; +import { useRefreshCases } from './use_on_refresh_cases'; -interface OwnProps { +interface Props { data: Cases; enableBulkActions: boolean; filterOptions: FilterOptions; - handleIsLoading: (a: boolean) => void; - refreshCases?: (a?: boolean) => void; selectedCases: Case[]; + deselectCases: () => void; } -type Props = OwnProps; +export const getStatusToasterMessage = (status: CaseStatuses, cases: Case[]): string => { + const totalCases = cases.length; + const caseTitle = totalCases === 1 ? cases[0].title : ''; + + if (status === CaseStatuses.open) { + return i18n.REOPENED_CASES({ totalCases, caseTitle }); + } else if (status === CaseStatuses['in-progress']) { + return i18n.MARK_IN_PROGRESS_CASES({ totalCases, caseTitle }); + } else if (status === CaseStatuses.closed) { + return i18n.CLOSED_CASES({ totalCases, caseTitle }); + } + + return ''; +}; export const CasesTableUtilityBar: FunctionComponent = ({ data, enableBulkActions = false, filterOptions, - handleIsLoading, - refreshCases, selectedCases, + deselectCases, }) => { - const [deleteCases, setDeleteCases] = useState([]); - - // Delete case - const { - dispatchResetIsDeleted, - handleOnDeleteConfirm, - handleToggleModal, - isLoading: isDeleting, - isDeleted, - isDisplayConfirmDeleteModal, - } = useDeleteCases(); + const [isModalVisible, setIsModalVisible] = useState(false); + const onCloseModal = useCallback(() => setIsModalVisible(false), []); + const refreshCases = useRefreshCases(); - // Update case - const { - dispatchResetIsUpdated, - isLoading: isUpdating, - isUpdated, - updateBulkStatus, - } = useUpdateCases(); + const { mutate: deleteCases } = useDeleteCases(); + const { mutate: updateCases } = useUpdateCases(); - useEffect(() => { - handleIsLoading(isDeleting); - }, [handleIsLoading, isDeleting]); + const toggleBulkDeleteModal = useCallback((cases: Case[]) => { + setIsModalVisible(true); + }, []); - useEffect(() => { - handleIsLoading(isUpdating); - }, [handleIsLoading, isUpdating]); - useEffect(() => { - if (isDeleted) { - if (refreshCases != null) refreshCases(); - dispatchResetIsDeleted(); - } - if (isUpdated) { - if (refreshCases != null) refreshCases(); - dispatchResetIsUpdated(); - } - }, [isDeleted, isUpdated, refreshCases, dispatchResetIsDeleted, dispatchResetIsUpdated]); - - const toggleBulkDeleteModal = useCallback( - (cases: Case[]) => { - handleToggleModal(); - - const convertToDeleteCases: DeleteCase[] = cases.map(({ id, title }) => ({ - id, - title, + const handleUpdateCaseStatus = useCallback( + (status: CaseStatuses) => { + const casesToUpdate = selectedCases.map((theCase) => ({ + status, + id: theCase.id, + version: theCase.version, })); - setDeleteCases(convertToDeleteCases); - }, - [setDeleteCases, handleToggleModal] - ); - const handleUpdateCaseStatus = useCallback( - (status: string) => { - updateBulkStatus(selectedCases, status); + updateCases({ + cases: casesToUpdate, + successToasterTitle: getStatusToasterMessage(status, selectedCases), + }); }, - [selectedCases, updateBulkStatus] + [selectedCases, updateCases] ); + const getBulkItemsPopoverContent = useCallback( (closePopover: () => void) => ( = ({ [selectedCases, filterOptions.status, toggleBulkDeleteModal, handleUpdateCaseStatus] ); + const onConfirmDeletion = useCallback(() => { + setIsModalVisible(false); + deleteCases({ + caseIds: selectedCases.map(({ id }) => id), + successToasterTitle: i18n.DELETED_CASES(selectedCases.length), + }); + }, [deleteCases, selectedCases]); + + const onRefresh = useCallback(() => { + deselectCases(); + refreshCases(); + }, [deselectCases, refreshCases]); + return ( @@ -141,20 +137,20 @@ export const CasesTableUtilityBar: FunctionComponent = ({ {i18n.REFRESH} - + {isModalVisible ? ( + + ) : null} ); }; diff --git a/x-pack/plugins/cases/public/components/bulk_actions/index.tsx b/x-pack/plugins/cases/public/components/bulk_actions/index.tsx index 8cf3a14ffd3c..fcf2002f8882 100644 --- a/x-pack/plugins/cases/public/components/bulk_actions/index.tsx +++ b/x-pack/plugins/cases/public/components/bulk_actions/index.tsx @@ -19,7 +19,7 @@ interface GetBulkItems { closePopover: () => void; deleteCasesAction: (cases: Case[]) => void; selectedCases: Case[]; - updateCaseStatus: (status: string) => void; + updateCaseStatus: (status: CaseStatuses) => void; } export const getBulkItems = ({ diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx index 8735b94d71b2..cf15d546415b 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx @@ -8,13 +8,14 @@ import React from 'react'; import { mount } from 'enzyme'; -import { useDeleteCases } from '../../containers/use_delete_cases'; import { noDeleteCasesPermissions, TestProviders } from '../../common/mock'; import { basicCase, basicPush } from '../../containers/mock'; import { Actions } from './actions'; import * as i18n from '../case_view/translations'; -jest.mock('../../containers/use_delete_cases'); -const useDeleteCasesMock = useDeleteCases as jest.Mock; +import * as api from '../../containers/api'; +import { waitFor } from '@testing-library/dom'; + +jest.mock('../../containers/api'); jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -26,6 +27,7 @@ jest.mock('react-router-dom', () => { }), }; }); + const defaultProps = { allCasesNavigation: { href: 'all-cases-href', @@ -34,23 +36,10 @@ const defaultProps = { caseData: basicCase, currentExternalIncident: null, }; -describe('CaseView actions', () => { - const handleOnDeleteConfirm = jest.fn(); - const handleToggleModal = jest.fn(); - const dispatchResetIsDeleted = jest.fn(); - const defaultDeleteState = { - dispatchResetIsDeleted, - handleToggleModal, - handleOnDeleteConfirm, - isLoading: false, - isError: false, - isDeleted: false, - isDisplayConfirmDeleteModal: false, - }; +describe('CaseView actions', () => { beforeEach(() => { jest.resetAllMocks(); - useDeleteCasesMock.mockImplementation(() => defaultDeleteState); }); it('clicking trash toggles modal', () => { @@ -61,10 +50,9 @@ describe('CaseView actions', () => { ); expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="property-actions-ellipses"]').first().simulate('click'); wrapper.find('button[data-test-subj="property-actions-trash"]').simulate('click'); - expect(handleToggleModal).toHaveBeenCalled(); + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy(); }); it('does not show trash icon when user does not have deletion privileges', () => { @@ -78,22 +66,26 @@ describe('CaseView actions', () => { expect(wrapper.find('button[data-test-subj="property-actions-ellipses"]').exists()).toBeFalsy(); }); - it('toggle delete modal and confirm', () => { - useDeleteCasesMock.mockImplementation(() => ({ - ...defaultDeleteState, - isDisplayConfirmDeleteModal: true, - })); + it('toggle delete modal and confirm', async () => { + const deleteCasesSpy = jest + .spyOn(api, 'deleteCases') + .mockRejectedValue(new Error('useDeleteCases: Test error')); + const wrapper = mount( ); + wrapper.find('button[data-test-subj="property-actions-ellipses"]').first().simulate('click'); + wrapper.find('button[data-test-subj="property-actions-trash"]').simulate('click'); + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy(); wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); - expect(handleOnDeleteConfirm.mock.calls[0][0]).toEqual([ - { id: basicCase.id, title: basicCase.title }, - ]); + + await waitFor(() => { + expect(deleteCasesSpy).toHaveBeenCalledWith(['basic-case-id'], expect.anything()); + }); }); it('displays active incident link', () => { diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx index 975cf89fc103..2481822f76b8 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx @@ -6,7 +6,7 @@ */ import { isEmpty } from 'lodash/fp'; -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiFlexItem } from '@elastic/eui'; import * as i18n from '../case_view/translations'; import { useDeleteCases } from '../../containers/use_delete_cases'; @@ -23,11 +23,18 @@ interface CaseViewActions { } const ActionsComponent: React.FC = ({ caseData, currentExternalIncident }) => { - // Delete case - const { handleToggleModal, handleOnDeleteConfirm, isDeleted, isDisplayConfirmDeleteModal } = - useDeleteCases(); + const { mutate: deleteCases } = useDeleteCases(); const { navigateToAllCases } = useAllCasesNavigation(); const { permissions } = useCasesContext(); + const [isModalVisible, setIsModalVisible] = useState(false); + + const openModal = useCallback(() => { + setIsModalVisible(true); + }, []); + + const closeModal = useCallback(() => { + setIsModalVisible(false); + }, []); const propertyActions = useMemo( () => [ @@ -36,7 +43,7 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal { iconType: 'trash', label: i18n.DELETE_CASE(), - onClick: handleToggleModal, + onClick: openModal, }, ] : []), @@ -50,13 +57,16 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal ] : []), ], - [handleToggleModal, currentExternalIncident, permissions.delete] + [permissions.delete, openModal, currentExternalIncident] ); - if (isDeleted) { - navigateToAllCases(); - return null; - } + const onConfirmDeletion = useCallback(() => { + setIsModalVisible(false); + deleteCases( + { caseIds: [caseData.id], successToasterTitle: i18n.DELETED_CASES(1) }, + { onSuccess: navigateToAllCases } + ); + }, [caseData.id, deleteCases, navigateToAllCases]); if (propertyActions.length === 0) { return null; @@ -65,12 +75,13 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal return ( - + {isModalVisible ? ( + + ) : null} ); }; diff --git a/x-pack/plugins/cases/public/components/case_view/index.test.tsx b/x-pack/plugins/cases/public/components/case_view/index.test.tsx index 20cf2c8d0d9b..6a5fd8fad811 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.test.tsx @@ -30,7 +30,7 @@ import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; import CaseView from '.'; import { waitFor } from '@testing-library/dom'; import { useGetTags } from '../../containers/use_get_tags'; -import { CASE_VIEW_CACHE_KEY } from '../../containers/constants'; +import { casesQueriesKeys } from '../../containers/constants'; import { alertsHit, caseViewProps, @@ -173,7 +173,8 @@ describe('CaseView', () => { const queryClientSpy = jest.spyOn(appMockRenderer.queryClient, 'invalidateQueries'); const result = appMockRenderer.render(); userEvent.click(result.getByTestId('case-refresh')); - expect(queryClientSpy).toHaveBeenCalledWith(['case']); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.caseView()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); }); describe('when a `refreshRef` prop is provided', () => { @@ -205,7 +206,8 @@ describe('CaseView', () => { it('should refresh actions and comments', async () => { refreshRef!.current!.refreshCase(); await waitFor(() => { - expect(queryClientSpy).toHaveBeenCalledWith([CASE_VIEW_CACHE_KEY]); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.caseView()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); }); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx index 396f180a074a..d12ddc258e76 100644 --- a/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx @@ -7,7 +7,7 @@ import { useCallback } from 'react'; import { useQueryClient } from '@tanstack/react-query'; -import { CASE_TAGS_CACHE_KEY, CASE_VIEW_CACHE_KEY } from '../../containers/constants'; +import { casesQueriesKeys } from '../../containers/constants'; /** * Using react-query queryClient to invalidate all the @@ -17,10 +17,11 @@ import { CASE_TAGS_CACHE_KEY, CASE_VIEW_CACHE_KEY } from '../../containers/const * forces the page to fetch all the data again. Including * metrics, actions, comments, etc. */ + export const useRefreshCaseViewPage = () => { const queryClient = useQueryClient(); return useCallback(() => { - queryClient.invalidateQueries([CASE_VIEW_CACHE_KEY]); - queryClient.invalidateQueries([CASE_TAGS_CACHE_KEY]); + queryClient.invalidateQueries(casesQueriesKeys.caseView()); + queryClient.invalidateQueries(casesQueriesKeys.tags()); }, [queryClient]); }; diff --git a/x-pack/plugins/cases/public/components/confirm_delete_case/index.tsx b/x-pack/plugins/cases/public/components/confirm_delete_case/index.tsx index ce8287310fb1..e1c4e0c1fa02 100644 --- a/x-pack/plugins/cases/public/components/confirm_delete_case/index.tsx +++ b/x-pack/plugins/cases/public/components/confirm_delete_case/index.tsx @@ -10,35 +10,28 @@ import { EuiConfirmModal } from '@elastic/eui'; import * as i18n from './translations'; interface ConfirmDeleteCaseModalProps { - caseTitle: string; - isModalVisible: boolean; - caseQuantity?: number; + totalCasesToBeDeleted: number; onCancel: () => void; onConfirm: () => void; } const ConfirmDeleteCaseModalComp: React.FC = ({ - caseTitle, - isModalVisible, - caseQuantity = 1, + totalCasesToBeDeleted, onCancel, onConfirm, }) => { - if (!isModalVisible) { - return null; - } return ( - {i18n.CONFIRM_QUESTION(caseQuantity)} + {i18n.CONFIRM_QUESTION(totalCasesToBeDeleted)} ); }; diff --git a/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts b/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts index f8e4ab2a83a7..e6f5072f08f8 100644 --- a/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts +++ b/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts @@ -14,12 +14,6 @@ export const DELETE_TITLE = (caseTitle: string) => defaultMessage: 'Delete "{caseTitle}"', }); -export const DELETE_SELECTED_CASES = (quantity: number, title: string) => - i18n.translate('xpack.cases.confirmDeleteCase.selectedCases', { - values: { quantity, title }, - defaultMessage: 'Delete "{quantity, plural, =1 {{title}} other {Selected {quantity} cases}}"', - }); - export const CONFIRM_QUESTION = (quantity: number) => i18n.translate('xpack.cases.confirmDeleteCase.confirmQuestion', { values: { quantity }, diff --git a/x-pack/plugins/cases/public/containers/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/__mocks__/api.ts index 2b4313512308..d01f927d3c32 100644 --- a/x-pack/plugins/cases/public/containers/__mocks__/api.ts +++ b/x-pack/plugins/cases/public/containers/__mocks__/api.ts @@ -8,7 +8,6 @@ import { ActionLicense, Cases, - BulkUpdateStatus, Case, CasesStatus, CaseUserActions, @@ -28,7 +27,7 @@ import { pushedCase, tags, } from '../mock'; -import { ResolvedCase, SeverityAll } from '../../../common/ui/types'; +import { CaseUpdateRequest, ResolvedCase, SeverityAll } from '../../../common/ui/types'; import { CasePatchRequest, CasePostRequest, @@ -99,8 +98,8 @@ export const patchCase = async ( signal: AbortSignal ): Promise => Promise.resolve([basicCase]); -export const patchCasesStatus = async ( - cases: BulkUpdateStatus[], +export const updateCases = async ( + cases: CaseUpdateRequest[], signal: AbortSignal ): Promise => Promise.resolve(allCases.cases); diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx index e51224dc593d..d5618ff8a7e9 100644 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ b/x-pack/plugins/cases/public/containers/api.test.tsx @@ -25,7 +25,7 @@ import { getCaseUserActions, getTags, patchCase, - patchCasesStatus, + updateCases, patchComment, postCase, createAttachments, @@ -449,7 +449,7 @@ describe('Cases API', () => { }); }); - describe('patchCasesStatus', () => { + describe('updateCases', () => { beforeEach(() => { fetchMock.mockClear(); fetchMock.mockResolvedValue(casesSnake); @@ -464,7 +464,7 @@ describe('Cases API', () => { ]; test('should be called with correct check url, method, signal', async () => { - await patchCasesStatus(data, abortCtrl.signal); + await updateCases(data, abortCtrl.signal); expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}`, { method: 'PATCH', body: JSON.stringify({ cases: data }), @@ -473,7 +473,7 @@ describe('Cases API', () => { }); test('should return correct response should not covert to camel case registered attachments', async () => { - const resp = await patchCasesStatus(data, abortCtrl.signal); + const resp = await updateCases(data, abortCtrl.signal); expect(resp).toEqual(cases); }); }); diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts index 697635030eb4..ff8d05ef653d 100644 --- a/x-pack/plugins/cases/public/containers/api.ts +++ b/x-pack/plugins/cases/public/containers/api.ts @@ -10,6 +10,7 @@ import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/const import { isEmpty } from 'lodash'; import { Cases, + CaseUpdateRequest, FetchCasesProps, ResolvedCase, SeverityAll, @@ -58,7 +59,6 @@ import { import { ActionLicense, - BulkUpdateStatus, Case, SingleCaseMetrics, SingleCaseMetricsFeature, @@ -238,8 +238,8 @@ export const patchCase = async ( return convertCasesToCamelCase(decodeCasesResponse(response)); }; -export const patchCasesStatus = async ( - cases: BulkUpdateStatus[], +export const updateCases = async ( + cases: CaseUpdateRequest[], signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch(CASES_URL, { diff --git a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx b/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx index 76a48e5d09ca..8503ccd6eddf 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx @@ -9,13 +9,13 @@ import { useQuery } from '@tanstack/react-query'; import * as i18n from '../translations'; import { fetchActionTypes } from './api'; import { useToasts } from '../../common/lib/kibana'; -import { CASE_CONFIGURATION_CACHE_KEY } from '../constants'; +import { casesQueriesKeys } from '../constants'; import { ServerError } from '../../types'; export const useGetActionTypes = () => { const toasts = useToasts(); return useQuery( - [CASE_CONFIGURATION_CACHE_KEY, 'actionTypes'], + casesQueriesKeys.connectorTypes(), () => { const abortController = new AbortController(); return fetchActionTypes({ signal: abortController.signal }); diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx index 95124af988fb..5e96dd86ae98 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx @@ -9,14 +9,14 @@ import { useQuery } from '@tanstack/react-query'; import { fetchConnectors } from './api'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import * as i18n from './translations'; -import { CASE_CONNECTORS_CACHE_KEY } from '../constants'; +import { casesQueriesKeys } from '../constants'; import { ServerError } from '../../types'; export function useGetConnectors() { const toasts = useToasts(); const { actions } = useApplicationCapabilities(); return useQuery( - [CASE_CONNECTORS_CACHE_KEY], + casesQueriesKeys.connectorsList(), async () => { if (!actions.read) { return []; diff --git a/x-pack/plugins/cases/public/containers/constants.ts b/x-pack/plugins/cases/public/containers/constants.ts index a87d77330344..a6acbbd68d41 100644 --- a/x-pack/plugins/cases/public/containers/constants.ts +++ b/x-pack/plugins/cases/public/containers/constants.ts @@ -5,23 +5,36 @@ * 2.0. */ +import { SingleCaseMetricsFeature } from './types'; + export const DEFAULT_TABLE_ACTIVE_PAGE = 1; export const DEFAULT_TABLE_LIMIT = 5; -export const CASE_VIEW_CACHE_KEY = 'case'; -export const CASE_VIEW_ACTIONS_CACHE_KEY = 'user-actions'; -export const CASE_VIEW_METRICS_CACHE_KEY = 'metrics'; -export const CASE_CONFIGURATION_CACHE_KEY = 'case-configuration'; -export const CASE_LIST_CACHE_KEY = 'case-list'; -export const CASE_CONNECTORS_CACHE_KEY = 'case-connectors'; -export const CASE_LICENSE_CACHE_KEY = 'case-license-action'; -export const CASE_TAGS_CACHE_KEY = 'case-tags'; - -/** - * User profiles - */ +export const casesQueriesKeys = { + all: ['cases'] as const, + users: ['users'] as const, + connectors: ['connectors'] as const, + connectorsList: () => [...casesQueriesKeys.connectors, 'list'] as const, + casesList: () => [...casesQueriesKeys.all, 'list'] as const, + casesMetrics: () => [...casesQueriesKeys.casesList(), 'metrics'] as const, + casesStatuses: () => [...casesQueriesKeys.casesList(), 'statuses'] as const, + cases: (params: unknown) => [...casesQueriesKeys.casesList(), 'all-cases', params] as const, + caseView: () => [...casesQueriesKeys.all, 'case'] as const, + case: (id: string) => [...casesQueriesKeys.caseView(), id] as const, + caseMetrics: (id: string, features: SingleCaseMetricsFeature[]) => + [...casesQueriesKeys.case(id), 'metrics', features] as const, + userActions: (id: string, connectorId: string) => + [...casesQueriesKeys.case(id), 'user-actions', connectorId] as const, + userProfiles: () => [...casesQueriesKeys.users, 'user-profiles'] as const, + userProfilesList: (ids: string[]) => [...casesQueriesKeys.userProfiles(), ids] as const, + currentUser: () => [...casesQueriesKeys.users, 'current-user'] as const, + suggestUsers: (params: unknown) => [...casesQueriesKeys.users, 'suggest', params] as const, + connectorTypes: () => [...casesQueriesKeys.connectors, 'types'] as const, + license: () => [...casesQueriesKeys.connectors, 'license'] as const, + tags: () => [...casesQueriesKeys.all, 'tags'] as const, +}; -export const USER_PROFILES_CACHE_KEY = 'user-profiles'; -export const USER_PROFILES_SUGGEST_CACHE_KEY = 'suggest'; -export const USER_PROFILES_BULK_GET_CACHE_KEY = 'bulk-get'; -export const USER_PROFILES_GET_CURRENT_CACHE_KEY = 'get-current'; +export const casesMutationsKeys = { + deleteCases: ['delete-cases'] as const, + updateCases: ['update-cases'] as const, +}; diff --git a/x-pack/plugins/cases/public/containers/translations.ts b/x-pack/plugins/cases/public/containers/translations.ts index 72aeb66772c5..5bf4acf385fc 100644 --- a/x-pack/plugins/cases/public/containers/translations.ts +++ b/x-pack/plugins/cases/public/containers/translations.ts @@ -23,48 +23,9 @@ export const UPDATED_CASE = (caseTitle: string) => defaultMessage: 'Updated "{caseTitle}"', }); -export const DELETED_CASES = (totalCases: number, caseTitle?: string) => - i18n.translate('xpack.cases.containers.deletedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Deleted {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const CLOSED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.closedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const REOPENED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.reopenedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Opened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const MARK_IN_PROGRESS_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.markInProgressCases', { - values: { caseTitle, totalCases }, - defaultMessage: - 'Marked {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}} as in progress', - }); +export const UPDATED_CASES = i18n.translate('xpack.cases.containers.updatedCases', { + defaultMessage: 'Updated cases', +}); export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = (serviceName: string) => i18n.translate('xpack.cases.containers.pushToExternalService', { diff --git a/x-pack/plugins/cases/public/containers/use_bulk_update_case.test.tsx b/x-pack/plugins/cases/public/containers/use_bulk_update_case.test.tsx index d00b361828a6..d46f79569622 100644 --- a/x-pack/plugins/cases/public/containers/use_bulk_update_case.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_bulk_update_case.test.tsx @@ -6,126 +6,89 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; -import { CaseStatuses } from '../../common/api'; -import { useUpdateCases, UseUpdateCases } from './use_bulk_update_case'; -import { basicCase } from './mock'; +import { useUpdateCases } from './use_bulk_update_case'; +import { allCases } from './mock'; +import { useToasts } from '../common/lib/kibana'; import * as api from './api'; +import { createAppMockRenderer, AppMockRenderer } from '../common/mock'; +import { casesQueriesKeys } from './constants'; jest.mock('./api'); jest.mock('../common/lib/kibana'); describe('useUpdateCases', () => { const abortCtrl = new AbortController(); + const addSuccess = jest.fn(); + const addError = jest.fn(); + + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); + + let appMockRender: AppMockRenderer; + beforeEach(() => { + appMockRender = createAppMockRenderer(); jest.clearAllMocks(); - jest.restoreAllMocks(); }); - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - isError: false, - isUpdated: false, - updateBulkStatus: result.current.updateBulkStatus, - dispatchResetIsUpdated: result.current.dispatchResetIsUpdated, - }); + it('calls the api when invoked with the correct parameters', async () => { + const spy = jest.spyOn(api, 'updateCases'); + const { waitForNextUpdate, result } = renderHook(() => useUpdateCases(), { + wrapper: appMockRender.AppWrapper, }); - }); - it('calls patchCase with correct arguments', async () => { - const spyOnPatchCases = jest.spyOn(api, 'patchCasesStatus'); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - await waitForNextUpdate(); - - result.current.updateBulkStatus([basicCase], CaseStatuses.closed); - await waitForNextUpdate(); - expect(spyOnPatchCases).toBeCalledWith( - [ - { - status: CaseStatuses.closed, - id: basicCase.id, - version: basicCase.version, - }, - ], - abortCtrl.signal - ); + act(() => { + result.current.mutate({ cases: allCases.cases, successToasterTitle: 'Success title' }); }); - }); - it('patch cases', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - await waitForNextUpdate(); - result.current.updateBulkStatus([basicCase], CaseStatuses.closed); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isUpdated: true, - isLoading: false, - isError: false, - updateBulkStatus: result.current.updateBulkStatus, - dispatchResetIsUpdated: result.current.dispatchResetIsUpdated, - }); - }); + await waitForNextUpdate(); + + expect(spy).toHaveBeenCalledWith(allCases.cases, abortCtrl.signal); }); - it('set isLoading to true when posting case', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - await waitForNextUpdate(); - result.current.updateBulkStatus([basicCase], CaseStatuses.closed); + it('invalidates the queries correctly', async () => { + const queryClientSpy = jest.spyOn(appMockRender.queryClient, 'invalidateQueries'); + const { waitForNextUpdate, result } = renderHook(() => useUpdateCases(), { + wrapper: appMockRender.AppWrapper, + }); - expect(result.current.isLoading).toBe(true); + act(() => { + result.current.mutate({ cases: allCases.cases, successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.casesList()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.userProfiles()); }); - it('dispatchResetIsUpdated resets is updated', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - - await waitForNextUpdate(); - result.current.updateBulkStatus([basicCase], CaseStatuses.closed); - await waitForNextUpdate(); - expect(result.current.isUpdated).toBeTruthy(); - result.current.dispatchResetIsUpdated(); - expect(result.current.isUpdated).toBeFalsy(); + it('shows a success toaster', async () => { + const { waitForNextUpdate, result } = renderHook(() => useUpdateCases(), { + wrapper: appMockRender.AppWrapper, + }); + + act(() => { + result.current.mutate({ cases: allCases.cases, successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(addSuccess).toHaveBeenCalledWith('Success title'); }); - it('unhappy path', async () => { - const spyOnPatchCases = jest.spyOn(api, 'patchCasesStatus'); - spyOnPatchCases.mockImplementation(() => { - throw new Error('Something went wrong'); + it('shows a toast error when the api return an error', async () => { + jest.spyOn(api, 'updateCases').mockRejectedValue(new Error('useUpdateCases: Test error')); + + const { waitForNextUpdate, result } = renderHook(() => useUpdateCases(), { + wrapper: appMockRender.AppWrapper, }); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useUpdateCases() - ); - await waitForNextUpdate(); - result.current.updateBulkStatus([basicCase], CaseStatuses.closed); - - expect(result.current).toEqual({ - isUpdated: false, - isLoading: false, - isError: true, - updateBulkStatus: result.current.updateBulkStatus, - dispatchResetIsUpdated: result.current.dispatchResetIsUpdated, - }); + act(() => { + result.current.mutate({ cases: allCases.cases, successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(addError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/cases/public/containers/use_bulk_update_case.tsx b/x-pack/plugins/cases/public/containers/use_bulk_update_case.tsx index 715b0c611c3b..e0866bf0166a 100644 --- a/x-pack/plugins/cases/public/containers/use_bulk_update_case.tsx +++ b/x-pack/plugins/cases/public/containers/use_bulk_update_case.tsx @@ -5,149 +5,42 @@ * 2.0. */ -import { useCallback, useReducer, useRef, useEffect } from 'react'; -import { CaseStatuses } from '../../common/api'; +import { useQueryClient, useMutation } from '@tanstack/react-query'; import * as i18n from './translations'; -import { patchCasesStatus } from './api'; -import { BulkUpdateStatus, Case } from './types'; -import { useToasts } from '../common/lib/kibana'; - -interface UpdateState { - isUpdated: boolean; - isLoading: boolean; - isError: boolean; -} -type Action = - | { type: 'FETCH_INIT' } - | { type: 'FETCH_SUCCESS'; payload: boolean } - | { type: 'FETCH_FAILURE' } - | { type: 'RESET_IS_UPDATED' }; - -const dataFetchReducer = (state: UpdateState, action: Action): UpdateState => { - switch (action.type) { - case 'FETCH_INIT': - return { - ...state, - isLoading: true, - isError: false, - }; - case 'FETCH_SUCCESS': - return { - ...state, - isLoading: false, - isError: false, - isUpdated: action.payload, - }; - case 'FETCH_FAILURE': - return { - ...state, - isLoading: false, - isError: true, - }; - case 'RESET_IS_UPDATED': - return { - ...state, - isUpdated: false, - }; - default: - return state; - } -}; -export interface UseUpdateCases extends UpdateState { - updateBulkStatus: (cases: Case[], status: string) => void; - dispatchResetIsUpdated: () => void; +import { updateCases } from './api'; +import { CaseUpdateRequest } from './types'; +import { useCasesToast } from '../common/use_cases_toast'; +import { ServerError } from '../types'; +import { casesQueriesKeys, casesMutationsKeys } from './constants'; + +interface MutationArgs { + cases: CaseUpdateRequest[]; + successToasterTitle: string; } -const getStatusToasterMessage = ( - status: CaseStatuses, - messageArgs: { - totalCases: number; - caseTitle?: string; - } -): string => { - if (status === CaseStatuses.open) { - return i18n.REOPENED_CASES(messageArgs); - } else if (status === CaseStatuses['in-progress']) { - return i18n.MARK_IN_PROGRESS_CASES(messageArgs); - } else if (status === CaseStatuses.closed) { - return i18n.CLOSED_CASES(messageArgs); - } - - return ''; -}; - -export const useUpdateCases = (): UseUpdateCases => { - const [state, dispatch] = useReducer(dataFetchReducer, { - isLoading: false, - isError: false, - isUpdated: false, - }); - const toasts = useToasts(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const dispatchUpdateCases = useCallback(async (cases: BulkUpdateStatus[], action: string) => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - - dispatch({ type: 'FETCH_INIT' }); - const patchResponse = await patchCasesStatus(cases, abortCtrlRef.current.signal); - - if (!isCancelledRef.current) { - const resultCount = Object.keys(patchResponse).length; - const firstTitle = patchResponse[0].title; - - dispatch({ type: 'FETCH_SUCCESS', payload: true }); - const messageArgs = { - totalCases: resultCount, - caseTitle: resultCount === 1 ? firstTitle : '', - }; +export const useUpdateCases = () => { + const queryClient = useQueryClient(); + const { showErrorToast, showSuccessToast } = useCasesToast(); - const message = - action === 'status' ? getStatusToasterMessage(patchResponse[0].status, messageArgs) : ''; - - toasts.addSuccess(message); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { title: i18n.ERROR_TITLE } - ); - } - dispatch({ type: 'FETCH_FAILURE' }); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const dispatchResetIsUpdated = useCallback(() => { - dispatch({ type: 'RESET_IS_UPDATED' }); - }, []); - - const updateBulkStatus = useCallback( - (cases: Case[], status: string) => { - const updateCasesStatus: BulkUpdateStatus[] = cases.map((theCase) => ({ - status, - id: theCase.id, - version: theCase.version, - })); - dispatchUpdateCases(updateCasesStatus, 'status'); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [] - ); - - useEffect( - () => () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); + return useMutation( + ({ cases }: MutationArgs) => { + const abortCtrlRef = new AbortController(); + return updateCases(cases, abortCtrlRef.signal); }, - [] + { + mutationKey: casesMutationsKeys.updateCases, + onSuccess: (_, { successToasterTitle }) => { + queryClient.invalidateQueries(casesQueriesKeys.casesList()); + queryClient.invalidateQueries(casesQueriesKeys.tags()); + queryClient.invalidateQueries(casesQueriesKeys.userProfiles()); + + showSuccessToast(successToasterTitle); + }, + onError: (error: ServerError) => { + showErrorToast(error, { title: i18n.ERROR_DELETING }); + }, + } ); - - return { ...state, updateBulkStatus, dispatchResetIsUpdated }; }; + +export type UseUpdateCases = ReturnType; diff --git a/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx index 88f6db42144f..623a01746e3c 100644 --- a/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx @@ -7,125 +7,88 @@ import { renderHook, act } from '@testing-library/react-hooks'; -import { useDeleteCases, UseDeleteCase } from './use_delete_cases'; +import { useDeleteCases } from './use_delete_cases'; import * as api from './api'; +import { useToasts } from '../common/lib/kibana'; +import { AppMockRenderer, createAppMockRenderer } from '../common/mock'; +import { casesQueriesKeys } from './constants'; jest.mock('./api'); jest.mock('../common/lib/kibana'); describe('useDeleteCases', () => { const abortCtrl = new AbortController(); - const deleteObj = [ - { id: '1', title: 'case 1' }, - { id: '2', title: 'case 2' }, - { id: '3', title: 'case 3' }, - ]; - const deleteArr = ['1', '2', '3']; - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isDisplayConfirmDeleteModal: false, - isLoading: false, - isError: false, - isDeleted: false, - dispatchResetIsDeleted: result.current.dispatchResetIsDeleted, - handleOnDeleteConfirm: result.current.handleOnDeleteConfirm, - handleToggleModal: result.current.handleToggleModal, - }); - }); - }); + const addSuccess = jest.fn(); + const addError = jest.fn(); - it('calls deleteCases with correct arguments', async () => { - const spyOnDeleteCases = jest.spyOn(api, 'deleteCases'); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); + let appMockRender: AppMockRenderer; - result.current.handleOnDeleteConfirm(deleteObj); - await waitForNextUpdate(); - expect(spyOnDeleteCases).toBeCalledWith(deleteArr, abortCtrl.signal); - }); + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); }); - it('deletes cases', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); - result.current.handleToggleModal(); - result.current.handleOnDeleteConfirm(deleteObj); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isDisplayConfirmDeleteModal: false, - isLoading: false, - isError: false, - isDeleted: true, - dispatchResetIsDeleted: result.current.dispatchResetIsDeleted, - handleOnDeleteConfirm: result.current.handleOnDeleteConfirm, - handleToggleModal: result.current.handleToggleModal, - }); + it('calls the api when invoked with the correct parameters', async () => { + const spy = jest.spyOn(api, 'deleteCases'); + const { waitForNextUpdate, result } = renderHook(() => useDeleteCases(), { + wrapper: appMockRender.AppWrapper, + }); + + act(() => { + result.current.mutate({ caseIds: ['1', '2'], successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(spy).toHaveBeenCalledWith(['1', '2'], abortCtrl.signal); }); - it('resets is deleting', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); - result.current.handleToggleModal(); - result.current.handleOnDeleteConfirm(deleteObj); - await waitForNextUpdate(); - expect(result.current.isDeleted).toBeTruthy(); - result.current.handleToggleModal(); - result.current.dispatchResetIsDeleted(); - expect(result.current.isDeleted).toBeFalsy(); + it('invalidates the queries correctly', async () => { + const queryClientSpy = jest.spyOn(appMockRender.queryClient, 'invalidateQueries'); + const { waitForNextUpdate, result } = renderHook(() => useDeleteCases(), { + wrapper: appMockRender.AppWrapper, + }); + + act(() => { + result.current.mutate({ caseIds: ['1', '2'], successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.casesList()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.tags()); + expect(queryClientSpy).toHaveBeenCalledWith(casesQueriesKeys.userProfiles()); }); - it('set isLoading to true when deleting cases', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); - result.current.handleToggleModal(); - result.current.handleOnDeleteConfirm(deleteObj); - expect(result.current.isLoading).toBe(true); + it('shows a success toaster', async () => { + const { waitForNextUpdate, result } = renderHook(() => useDeleteCases(), { + wrapper: appMockRender.AppWrapper, }); + + act(() => { + result.current.mutate({ caseIds: ['1', '2'], successToasterTitle: 'Success title' }); + }); + + await waitForNextUpdate(); + + expect(addSuccess).toHaveBeenCalledWith('Success title'); }); - it('unhappy path', async () => { - const spyOnDeleteCases = jest.spyOn(api, 'deleteCases'); - spyOnDeleteCases.mockImplementation(() => { - throw new Error('Something went wrong'); + it('shows a toast error when the api return an error', async () => { + jest.spyOn(api, 'deleteCases').mockRejectedValue(new Error('useDeleteCases: Test error')); + + const { waitForNextUpdate, result } = renderHook(() => useDeleteCases(), { + wrapper: appMockRender.AppWrapper, }); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useDeleteCases() - ); - await waitForNextUpdate(); - result.current.handleToggleModal(); - result.current.handleOnDeleteConfirm(deleteObj); - - expect(result.current).toEqual({ - isDisplayConfirmDeleteModal: false, - isLoading: false, - isError: true, - isDeleted: false, - dispatchResetIsDeleted: result.current.dispatchResetIsDeleted, - handleOnDeleteConfirm: result.current.handleOnDeleteConfirm, - handleToggleModal: result.current.handleToggleModal, - }); + act(() => { + result.current.mutate({ caseIds: ['1', '2'], successToasterTitle: 'Success title' }); }); + + await waitForNextUpdate(); + + expect(addError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/cases/public/containers/use_delete_cases.tsx b/x-pack/plugins/cases/public/containers/use_delete_cases.tsx index 7ccec4436ec0..da2258f8f5d8 100644 --- a/x-pack/plugins/cases/public/containers/use_delete_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_delete_cases.tsx @@ -5,139 +5,41 @@ * 2.0. */ -import { useCallback, useReducer, useRef, useEffect } from 'react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import * as i18n from './translations'; import { deleteCases } from './api'; -import { DeleteCase } from './types'; -import { useToasts } from '../common/lib/kibana'; +import { ServerError } from '../types'; +import { casesQueriesKeys, casesMutationsKeys } from './constants'; +import { useCasesToast } from '../common/use_cases_toast'; -interface DeleteState { - isDisplayConfirmDeleteModal: boolean; - isDeleted: boolean; - isLoading: boolean; - isError: boolean; +interface MutationArgs { + caseIds: string[]; + successToasterTitle: string; } -type Action = - | { type: 'DISPLAY_MODAL'; payload: boolean } - | { type: 'FETCH_INIT' } - | { type: 'FETCH_SUCCESS'; payload: boolean } - | { type: 'FETCH_FAILURE' } - | { type: 'RESET_IS_DELETED' }; -const dataFetchReducer = (state: DeleteState, action: Action): DeleteState => { - switch (action.type) { - case 'DISPLAY_MODAL': - return { - ...state, - isDisplayConfirmDeleteModal: action.payload, - }; - case 'FETCH_INIT': - return { - ...state, - isLoading: true, - isError: false, - }; - case 'FETCH_SUCCESS': - return { - ...state, - isLoading: false, - isError: false, - isDeleted: action.payload, - }; - case 'FETCH_FAILURE': - return { - ...state, - isLoading: false, - isError: true, - }; - case 'RESET_IS_DELETED': - return { - ...state, - isDeleted: false, - }; - default: - return state; - } -}; - -export interface UseDeleteCase extends DeleteState { - dispatchResetIsDeleted: () => void; - handleOnDeleteConfirm: (cases: DeleteCase[]) => void; - handleToggleModal: () => void; -} - -export const useDeleteCases = (): UseDeleteCase => { - const [state, dispatch] = useReducer(dataFetchReducer, { - isDisplayConfirmDeleteModal: false, - isLoading: false, - isError: false, - isDeleted: false, - }); - const toasts = useToasts(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const dispatchDeleteCases = useCallback(async (cases: DeleteCase[]) => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - dispatch({ type: 'FETCH_INIT' }); - - const caseIds = cases.map((theCase) => theCase.id); - if (cases.length > 0) { - await deleteCases(caseIds, abortCtrlRef.current.signal); - } - - if (!isCancelledRef.current) { - dispatch({ type: 'FETCH_SUCCESS', payload: true }); - toasts.addSuccess( - i18n.DELETED_CASES(cases.length, cases.length === 1 ? cases[0].title : '') - ); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { title: i18n.ERROR_DELETING } - ); - } - dispatch({ type: 'FETCH_FAILURE' }); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); +export const useDeleteCases = () => { + const queryClient = useQueryClient(); + const { showErrorToast, showSuccessToast } = useCasesToast(); - const dispatchToggleDeleteModal = useCallback(() => { - dispatch({ type: 'DISPLAY_MODAL', payload: !state.isDisplayConfirmDeleteModal }); - }, [state.isDisplayConfirmDeleteModal]); - - const dispatchResetIsDeleted = useCallback(() => { - dispatch({ type: 'RESET_IS_DELETED' }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isDisplayConfirmDeleteModal]); - - const handleOnDeleteConfirm = useCallback( - (cases: DeleteCase[]) => { - dispatchDeleteCases(cases); - dispatchToggleDeleteModal(); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [state.isDisplayConfirmDeleteModal] - ); - const handleToggleModal = useCallback(() => { - dispatchToggleDeleteModal(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isDisplayConfirmDeleteModal]); - - useEffect( - () => () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); + return useMutation( + ({ caseIds }: MutationArgs) => { + const abortCtrlRef = new AbortController(); + return deleteCases(caseIds, abortCtrlRef.signal); }, - [] + { + mutationKey: casesMutationsKeys.deleteCases, + onSuccess: (_, { successToasterTitle }) => { + queryClient.invalidateQueries(casesQueriesKeys.casesList()); + queryClient.invalidateQueries(casesQueriesKeys.tags()); + queryClient.invalidateQueries(casesQueriesKeys.userProfiles()); + + showSuccessToast(successToasterTitle); + }, + onError: (error: ServerError) => { + showErrorToast(error, { title: i18n.ERROR_DELETING }); + }, + } ); - - return { ...state, dispatchResetIsDeleted, handleOnDeleteConfirm, handleToggleModal }; }; + +export type UseDeleteCases = ReturnType; diff --git a/x-pack/plugins/cases/public/containers/use_get_action_license.tsx b/x-pack/plugins/cases/public/containers/use_get_action_license.tsx index 8e9aa28de440..7f05012cbbe6 100644 --- a/x-pack/plugins/cases/public/containers/use_get_action_license.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_action_license.tsx @@ -10,7 +10,7 @@ import { useToasts } from '../common/lib/kibana'; import { getActionLicense } from './api'; import * as i18n from './translations'; import { ConnectorTypes } from '../../common/api'; -import { CASE_LICENSE_CACHE_KEY } from './constants'; +import { casesQueriesKeys } from './constants'; import { ServerError } from '../types'; const MINIMUM_LICENSE_REQUIRED_CONNECTOR = ConnectorTypes.jira; @@ -18,7 +18,7 @@ const MINIMUM_LICENSE_REQUIRED_CONNECTOR = ConnectorTypes.jira; export const useGetActionLicense = () => { const toasts = useToasts(); return useQuery( - [CASE_LICENSE_CACHE_KEY], + casesQueriesKeys.license(), async () => { const abortCtrl = new AbortController(); const response = await getActionLicense(abortCtrl.signal); diff --git a/x-pack/plugins/cases/public/containers/use_get_case.tsx b/x-pack/plugins/cases/public/containers/use_get_case.tsx index ded91240239a..bf588cc1e71d 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case.tsx @@ -11,12 +11,12 @@ import * as i18n from './translations'; import { useToasts } from '../common/lib/kibana'; import { resolveCase } from './api'; import { ServerError } from '../types'; -import { CASE_VIEW_CACHE_KEY } from './constants'; +import { casesQueriesKeys } from './constants'; export const useGetCase = (caseId: string) => { const toasts = useToasts(); return useQuery( - [CASE_VIEW_CACHE_KEY, caseId], + casesQueriesKeys.case(caseId), () => { const abortCtrlRef = new AbortController(); return resolveCase(caseId, true, abortCtrlRef.signal); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx index 1e294c4a5ba6..32d63fcc3b42 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx @@ -11,13 +11,13 @@ import { useToasts } from '../common/lib/kibana'; import { getSingleCaseMetrics } from './api'; import { ServerError } from '../types'; import { ERROR_TITLE } from './translations'; -import { CASE_VIEW_CACHE_KEY, CASE_VIEW_METRICS_CACHE_KEY } from './constants'; +import { casesQueriesKeys } from './constants'; export const useGetCaseMetrics = (caseId: string, features: SingleCaseMetricsFeature[]) => { const toasts = useToasts(); const abortCtrlRef = new AbortController(); return useQuery( - [CASE_VIEW_CACHE_KEY, CASE_VIEW_METRICS_CACHE_KEY, caseId, features], + casesQueriesKeys.caseMetrics(caseId, features), async () => { const response: SingleCaseMetrics = await getSingleCaseMetrics( caseId, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx index fde45207b673..c92d56b41ea7 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx @@ -20,7 +20,7 @@ import { import { ServerError } from '../types'; import { useToasts } from '../common/lib/kibana'; import { ERROR_TITLE } from './translations'; -import { CASE_VIEW_ACTIONS_CACHE_KEY, CASE_VIEW_CACHE_KEY } from './constants'; +import { casesQueriesKeys } from './constants'; export interface CaseService extends CaseExternalService { firstPushIndex: number; @@ -238,7 +238,7 @@ export const useGetCaseUserActions = (caseId: string, caseConnectorId: string) = const toasts = useToasts(); const abortCtrlRef = new AbortController(); return useQuery( - [CASE_VIEW_CACHE_KEY, CASE_VIEW_ACTIONS_CACHE_KEY, caseId, caseConnectorId], + casesQueriesKeys.userActions(caseId, caseConnectorId), async () => { const response = await getCaseUserActions(caseId, abortCtrlRef.signal); const participants = !isEmpty(response) diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx index 7b046cac3f13..d630534957e5 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx @@ -6,7 +6,7 @@ */ import { useQuery, UseQueryResult } from '@tanstack/react-query'; -import { CASE_LIST_CACHE_KEY, DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; +import { casesQueriesKeys, DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; import { Cases, FilterOptions, QueryParams, SortFieldCase, StatusAll, SeverityAll } from './types'; import { useToasts } from '../common/lib/kibana'; import * as i18n from './translations'; @@ -51,7 +51,7 @@ export const useGetCases = ( ): UseQueryResult => { const toasts = useToasts(); return useQuery( - [CASE_LIST_CACHE_KEY, 'cases', params], + casesQueriesKeys.cases(params), () => { const abortCtrl = new AbortController(); return getCases({ diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_metrics.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_metrics.test.tsx index a8747a2bd43a..0b0cdc59a487 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_metrics.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_metrics.test.tsx @@ -5,124 +5,56 @@ * 2.0. */ -import React from 'react'; -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import * as api from '../api'; -import { TestProviders } from '../common/mock'; -import { useGetCasesMetrics, UseGetCasesMetrics } from './use_get_cases_metrics'; +import { AppMockRenderer, createAppMockRenderer } from '../common/mock'; +import { useGetCasesMetrics } from './use_get_cases_metrics'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; +import { useToasts } from '../common/lib/kibana'; jest.mock('../api'); jest.mock('../common/lib/kibana'); describe('useGetCasesMetrics', () => { - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); + const abortCtrl = new AbortController(); + const addSuccess = jest.fn(); + const addError = jest.fn(); - it('init', async () => { - const { result } = renderHook(() => useGetCasesMetrics(), { - wrapper: ({ children }) => {children}, - }); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - await act(async () => { - expect(result.current).toEqual({ - mttr: 0, - isLoading: true, - isError: false, - fetchCasesMetrics: result.current.fetchCasesMetrics, - }); - }); + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); }); - it('calls getCasesMetrics api', async () => { + it('calls the api when invoked with the correct parameters', async () => { const spy = jest.spyOn(api, 'getCasesMetrics'); - await act(async () => { - const { waitForNextUpdate } = renderHook( - () => useGetCasesMetrics(), - { - wrapper: ({ children }) => {children}, - } - ); - - await waitForNextUpdate(); - expect(spy).toBeCalledWith({ - http: expect.anything(), - signal: expect.anything(), - query: { - features: ['mttr'], - owner: [SECURITY_SOLUTION_OWNER], - }, - }); + const { waitForNextUpdate } = renderHook(() => useGetCasesMetrics(), { + wrapper: appMockRender.AppWrapper, }); - }); - it('fetch cases metrics', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => useGetCasesMetrics(), - { - wrapper: ({ children }) => {children}, - } - ); + await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - mttr: 12, - isLoading: false, - isError: false, - fetchCasesMetrics: result.current.fetchCasesMetrics, - }); + expect(spy).toHaveBeenCalledWith({ + http: expect.anything(), + signal: abortCtrl.signal, + query: { owner: [SECURITY_SOLUTION_OWNER], features: ['mttr'] }, }); }); - it('fetches metrics when fetchCasesMetrics is invoked', async () => { - const spy = jest.spyOn(api, 'getCasesMetrics'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => useGetCasesMetrics(), - { - wrapper: ({ children }) => {children}, - } - ); - - await waitForNextUpdate(); - expect(spy).toBeCalledWith({ - http: expect.anything(), - signal: expect.anything(), - query: { - features: ['mttr'], - owner: [SECURITY_SOLUTION_OWNER], - }, - }); - result.current.fetchCasesMetrics(); - await waitForNextUpdate(); - expect(spy).toHaveBeenCalledTimes(2); - }); - }); + it('shows a toast error when the api return an error', async () => { + jest + .spyOn(api, 'getCasesMetrics') + .mockRejectedValue(new Error('useGetCasesMetrics: Test error')); - it('unhappy path', async () => { - const spy = jest.spyOn(api, 'getCasesMetrics'); - spy.mockImplementation(() => { - throw new Error('Oh on. this is impossible'); + const { waitForNextUpdate } = renderHook(() => useGetCasesMetrics(), { + wrapper: appMockRender.AppWrapper, }); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => useGetCasesMetrics(), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); + await waitForNextUpdate(); - expect(result.current).toEqual({ - mttr: 0, - isLoading: false, - isError: true, - fetchCasesMetrics: result.current.fetchCasesMetrics, - }); - }); + expect(addError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_metrics.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_metrics.tsx index a5cb116acc55..b43266e55340 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_metrics.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_metrics.tsx @@ -5,88 +5,37 @@ * 2.0. */ -import { useCallback, useEffect, useState, useRef } from 'react'; - +import { useQuery } from '@tanstack/react-query'; import { useCasesContext } from '../components/cases_context/use_cases_context'; import * as i18n from './translations'; -import { useHttp, useToasts } from '../common/lib/kibana'; +import { useHttp } from '../common/lib/kibana'; import { getCasesMetrics } from '../api'; import { CasesMetrics } from './types'; +import { useCasesToast } from '../common/use_cases_toast'; +import { ServerError } from '../types'; +import { casesQueriesKeys } from './constants'; -interface CasesMetricsState extends CasesMetrics { - isLoading: boolean; - isError: boolean; -} - -const initialData: CasesMetricsState = { - mttr: 0, - isLoading: true, - isError: false, -}; - -export interface UseGetCasesMetrics extends CasesMetricsState { - fetchCasesMetrics: () => void; -} - -export const useGetCasesMetrics = (): UseGetCasesMetrics => { +export const useGetCasesMetrics = () => { const http = useHttp(); const { owner } = useCasesContext(); - const [casesMetricsState, setCasesMetricsState] = useState(initialData); - const toasts = useToasts(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const fetchCasesMetrics = useCallback(async () => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - setCasesMetricsState({ - ...initialData, - isLoading: true, - }); + const { showErrorToast } = useCasesToast(); - const response = await getCasesMetrics({ + return useQuery( + casesQueriesKeys.casesMetrics(), + () => { + const abortCtrlRef = new AbortController(); + return getCasesMetrics({ http, - signal: abortCtrlRef.current.signal, + signal: abortCtrlRef.signal, query: { owner, features: ['mttr'] }, }); - - if (!isCancelledRef.current) { - setCasesMetricsState({ - ...response, - isLoading: false, - isError: false, - }); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { title: i18n.ERROR_TITLE } - ); - } - setCasesMetricsState({ - mttr: 0, - isLoading: false, - isError: true, - }); - } + }, + { + onError: (error: ServerError) => { + showErrorToast(error, { title: i18n.ERROR_TITLE }); + }, } - }, [http, owner, toasts]); - - useEffect(() => { - fetchCasesMetrics(); - - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - }; - }, [fetchCasesMetrics]); - - return { - ...casesMetricsState, - fetchCasesMetrics, - }; + ); }; + +export type UseGetCasesMetrics = ReturnType; diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx index 3978e944db94..4f2572093a28 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx @@ -5,104 +5,56 @@ * 2.0. */ -import React from 'react'; -import { renderHook, act } from '@testing-library/react-hooks'; -import { useGetCasesStatus, UseGetCasesStatus } from './use_get_cases_status'; -import { casesStatus } from './mock'; +import { renderHook } from '@testing-library/react-hooks'; +import { useGetCasesStatus } from './use_get_cases_status'; import * as api from '../api'; -import { TestProviders } from '../common/mock'; +import { AppMockRenderer, createAppMockRenderer } from '../common/mock'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; +import { useToasts } from '../common/lib/kibana'; jest.mock('../api'); jest.mock('../common/lib/kibana'); -describe('useGetCasesStatus', () => { +describe('useGetCasesMetrics', () => { const abortCtrl = new AbortController(); + const addSuccess = jest.fn(); + const addError = jest.fn(); + + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); + + let appMockRender: AppMockRenderer; + beforeEach(() => { + appMockRender = createAppMockRenderer(); jest.clearAllMocks(); - jest.restoreAllMocks(); }); - it('init', async () => { - const { result } = renderHook(() => useGetCasesStatus(), { - wrapper: ({ children }) => {children}, - }); - - await act(async () => { - expect(result.current).toEqual({ - countClosedCases: 0, - countOpenCases: 0, - countInProgressCases: 0, - isLoading: true, - isError: false, - fetchCasesStatus: result.current.fetchCasesStatus, - }); + it('calls the api when invoked with the correct parameters', async () => { + const spy = jest.spyOn(api, 'getCasesStatus'); + const { waitForNextUpdate } = renderHook(() => useGetCasesStatus(), { + wrapper: appMockRender.AppWrapper, }); - }); - it('calls getCasesStatus api', async () => { - const spyOnGetCasesStatus = jest.spyOn(api, 'getCasesStatus'); - await act(async () => { - const { waitForNextUpdate } = renderHook( - () => useGetCasesStatus(), - { - wrapper: ({ children }) => {children}, - } - ); + await waitForNextUpdate(); - await waitForNextUpdate(); - expect(spyOnGetCasesStatus).toBeCalledWith({ - http: expect.anything(), - signal: abortCtrl.signal, - query: { owner: [SECURITY_SOLUTION_OWNER] }, - }); + expect(spy).toHaveBeenCalledWith({ + http: expect.anything(), + signal: abortCtrl.signal, + query: { owner: [SECURITY_SOLUTION_OWNER] }, }); }); - it('fetch statuses', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => useGetCasesStatus(), - { - wrapper: ({ children }) => {children}, - } - ); + it('shows a toast error when the api return an error', async () => { + jest + .spyOn(api, 'getCasesStatus') + .mockRejectedValue(new Error('useGetCasesMetrics: Test error')); - await waitForNextUpdate(); - expect(result.current).toEqual({ - countClosedCases: casesStatus.countClosedCases, - countOpenCases: casesStatus.countOpenCases, - countInProgressCases: casesStatus.countInProgressCases, - isLoading: false, - isError: false, - fetchCasesStatus: result.current.fetchCasesStatus, - }); + const { waitForNextUpdate } = renderHook(() => useGetCasesStatus(), { + wrapper: appMockRender.AppWrapper, }); - }); - it('unhappy path', async () => { - const spyOnGetCasesStatus = jest.spyOn(api, 'getCasesStatus'); - spyOnGetCasesStatus.mockImplementation(() => { - throw new Error('Something went wrong'); - }); + await waitForNextUpdate(); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => useGetCasesStatus(), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - countClosedCases: 0, - countOpenCases: 0, - countInProgressCases: 0, - isLoading: false, - isError: true, - fetchCasesStatus: result.current.fetchCasesStatus, - }); - }); + expect(addError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx index 6530236a2fee..c2ba6659edcb 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx @@ -5,94 +5,37 @@ * 2.0. */ -import { useCallback, useEffect, useState, useRef } from 'react'; - +import { useQuery } from '@tanstack/react-query'; import { useCasesContext } from '../components/cases_context/use_cases_context'; import * as i18n from './translations'; import { CasesStatus } from './types'; -import { useHttp, useToasts } from '../common/lib/kibana'; +import { useHttp } from '../common/lib/kibana'; import { getCasesStatus } from '../api'; +import { useCasesToast } from '../common/use_cases_toast'; +import { ServerError } from '../types'; +import { casesQueriesKeys } from './constants'; -interface CasesStatusState extends CasesStatus { - isLoading: boolean; - isError: boolean; -} - -const initialData: CasesStatusState = { - countClosedCases: 0, - countInProgressCases: 0, - countOpenCases: 0, - isLoading: true, - isError: false, -}; - -export interface UseGetCasesStatus extends CasesStatusState { - fetchCasesStatus: () => void; -} - -export const useGetCasesStatus = (): UseGetCasesStatus => { +export const useGetCasesStatus = () => { const http = useHttp(); const { owner } = useCasesContext(); - const [casesStatusState, setCasesStatusState] = useState(initialData); - const toasts = useToasts(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const fetchCasesStatus = useCallback(async () => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - setCasesStatusState({ - ...initialData, - isLoading: true, - }); + const { showErrorToast } = useCasesToast(); - const response = await getCasesStatus({ + return useQuery( + casesQueriesKeys.casesStatuses(), + () => { + const abortCtrlRef = new AbortController(); + return getCasesStatus({ http, - signal: abortCtrlRef.current.signal, + signal: abortCtrlRef.signal, query: { owner }, }); - - if (!isCancelledRef.current) { - setCasesStatusState({ - ...response, - isLoading: false, - isError: false, - }); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { title: i18n.ERROR_TITLE } - ); - } - setCasesStatusState({ - countClosedCases: 0, - countInProgressCases: 0, - countOpenCases: 0, - isLoading: false, - isError: true, - }); - } + }, + { + onError: (error: ServerError) => { + showErrorToast(error, { title: i18n.ERROR_TITLE }); + }, } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - fetchCasesStatus(); - - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return { - ...casesStatusState, - fetchCasesStatus, - }; + ); }; + +export type UseGetCasesStatus = ReturnType; diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.tsx index 1696a9d1413a..da56521536cb 100644 --- a/x-pack/plugins/cases/public/containers/use_get_tags.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_tags.tsx @@ -10,15 +10,14 @@ import { useToasts } from '../common/lib/kibana'; import { useCasesContext } from '../components/cases_context/use_cases_context'; import { ServerError } from '../types'; import { getTags } from './api'; -import { CASE_TAGS_CACHE_KEY } from './constants'; +import { casesQueriesKeys } from './constants'; import * as i18n from './translations'; -export const useGetTags = (cacheKey?: string) => { +export const useGetTags = () => { const toasts = useToasts(); const { owner } = useCasesContext(); - const key = [...(cacheKey ? [cacheKey] : []), CASE_TAGS_CACHE_KEY]; return useQuery( - key, + casesQueriesKeys.tags(), () => { const abortCtrl = new AbortController(); return getTags(abortCtrl.signal, owner); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts index de180b5970f3..b2928295dbb3 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts @@ -10,7 +10,7 @@ import { UserProfileWithAvatar } from '@kbn/user-profile-components'; import * as i18n from '../translations'; import { useKibana, useToasts } from '../../common/lib/kibana'; import { ServerError } from '../../types'; -import { USER_PROFILES_CACHE_KEY, USER_PROFILES_BULK_GET_CACHE_KEY } from '../constants'; +import { casesQueriesKeys } from '../constants'; import { bulkGetUserProfiles } from './api'; const profilesToMap = (profiles: UserProfileWithAvatar[]): Map => @@ -25,7 +25,7 @@ export const useBulkGetUserProfiles = ({ uids }: { uids: string[] }) => { const toasts = useToasts(); return useQuery>( - [USER_PROFILES_CACHE_KEY, USER_PROFILES_BULK_GET_CACHE_KEY, uids], + casesQueriesKeys.userProfilesList(uids), () => { return bulkGetUserProfiles({ security, uids }); }, diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.ts index 37c29fa0b2d0..d6e348367255 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.ts @@ -10,7 +10,7 @@ import { UserProfile } from '@kbn/security-plugin/common'; import * as i18n from '../translations'; import { useKibana, useToasts } from '../../common/lib/kibana'; import { ServerError } from '../../types'; -import { USER_PROFILES_CACHE_KEY, USER_PROFILES_GET_CURRENT_CACHE_KEY } from '../constants'; +import { casesQueriesKeys } from '../constants'; import { getCurrentUserProfile } from './api'; export const useGetCurrentUserProfile = () => { @@ -19,7 +19,7 @@ export const useGetCurrentUserProfile = () => { const toasts = useToasts(); return useQuery( - [USER_PROFILES_CACHE_KEY, USER_PROFILES_GET_CURRENT_CACHE_KEY], + casesQueriesKeys.currentUser(), () => { return getCurrentUserProfile({ security }); }, diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts index 26e03d0163c8..74c492850acd 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts @@ -14,7 +14,7 @@ import { DEFAULT_USER_SIZE, SEARCH_DEBOUNCE_MS } from '../../../common/constants import * as i18n from '../translations'; import { useKibana, useToasts } from '../../common/lib/kibana'; import { ServerError } from '../../types'; -import { USER_PROFILES_CACHE_KEY, USER_PROFILES_SUGGEST_CACHE_KEY } from '../constants'; +import { casesQueriesKeys } from '../constants'; import { suggestUserProfiles, SuggestUserProfilesArgs } from './api'; type Props = Omit & { onDebounce?: () => void }; @@ -49,11 +49,7 @@ export const useSuggestUserProfiles = ({ const toasts = useToasts(); return useQuery( - [ - USER_PROFILES_CACHE_KEY, - USER_PROFILES_SUGGEST_CACHE_KEY, - { name: debouncedName, owners, size }, - ], + casesQueriesKeys.suggestUsers({ name: debouncedName, owners, size }), () => { const abortCtrlRef = new AbortController(); return suggestUserProfiles({ diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index e0ff0ef2793f..ce86f1de798d 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -9286,7 +9286,6 @@ "xpack.cases.confirmDeleteCase.confirmQuestion": "Si vous supprimez {quantity, plural, =1 {ce cas} other {ces cas}}, toutes les données des cas connexes seront définitivement retirées et vous ne pourrez plus transmettre de données à un système de gestion des incidents externes. Voulez-vous vraiment continuer ?", "xpack.cases.confirmDeleteCase.deleteCase": "Supprimer {quantity, plural, =1 {le cas} other {les cas}}", "xpack.cases.confirmDeleteCase.deleteTitle": "Supprimer \"{caseTitle}\"", - "xpack.cases.confirmDeleteCase.selectedCases": "Supprimer \"{quantity, plural, =1 {{title}} other {{quantity} cas sélectionnés}}\"", "xpack.cases.connecors.get.missingCaseConnectorErrorMessage": "Le type d'objet \"{id}\" n'est pas enregistré.", "xpack.cases.connecors.register.duplicateCaseConnectorErrorMessage": "Le type d'objet \"{id}\" est déjà enregistré.", "xpack.cases.connectors.card.createCommentWarningDesc": "Configurez les champs Create Comment URL et Create Comment Objects pour que le connecteur {connectorName} puisse partager les commentaires.", @@ -9296,7 +9295,6 @@ "xpack.cases.connectors.cases.externalIncidentUpdated": "(mis à jour le {date} par {user})", "xpack.cases.connectors.jira.unableToGetIssueMessage": "Impossible d'obtenir le problème ayant l'ID {id}", "xpack.cases.containers.closedCases": "Fermeture de {totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases} cas}} effectuée", - "xpack.cases.containers.deletedCases": "Suppression de {totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases} cas}} effectuée", "xpack.cases.containers.markInProgressCases": "Marquage effectué de {totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases} cas}} comme étant en cours", "xpack.cases.containers.pushToExternalService": "Envoyé avec succès à { serviceName }", "xpack.cases.containers.reopenedCases": "Ouverture de {totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases} cas}} effectuée", @@ -9328,7 +9326,6 @@ "xpack.cases.caseTable.changeStatus": "Modifier le statut", "xpack.cases.caseTable.closed": "Fermé", "xpack.cases.caseTable.closedCases": "Cas fermés", - "xpack.cases.caseTable.delete": "Supprimer", "xpack.cases.caseTable.incidentSystem": "Système de gestion des incidents", "xpack.cases.caseTable.inProgressCases": "Cas en cours", "xpack.cases.caseTable.noCases.body": "Créer un cas ou modifiez vos filtres.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8715883e4251..5b56cc076172 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9273,7 +9273,6 @@ "xpack.cases.confirmDeleteCase.confirmQuestion": "{quantity, plural, =1 {このケース} other {これらのケース}}を削除すると、関連するすべてのケースデータが完全に削除され、外部インシデント管理システムにデータをプッシュできなくなります。続行していいですか?", "xpack.cases.confirmDeleteCase.deleteCase": "{quantity, plural, other {ケース}}を削除", "xpack.cases.confirmDeleteCase.deleteTitle": "「{caseTitle}」を削除", - "xpack.cases.confirmDeleteCase.selectedCases": "\"{quantity, plural, =1 {{title}} other {選択した{quantity}個のケース}}\"を削除", "xpack.cases.connecors.get.missingCaseConnectorErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。", "xpack.cases.connecors.register.duplicateCaseConnectorErrorMessage": "オブジェクトタイプ「{id}」はすでに登録されています。", "xpack.cases.connectors.card.createCommentWarningDesc": "コメントを外部で共有するには、{connectorName}コネクターの[コメントURLを作成]および[コメントオブジェクトを作成]フィールドを構成します。", @@ -9283,7 +9282,6 @@ "xpack.cases.connectors.cases.externalIncidentUpdated": "({date}に{user}が更新)", "xpack.cases.connectors.jira.unableToGetIssueMessage": "ID {id}の問題を取得できません", "xpack.cases.containers.closedCases": "{totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases}件のケース}}をクローズしました", - "xpack.cases.containers.deletedCases": "{totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases}件のケース}}を削除しました", "xpack.cases.containers.markInProgressCases": "{totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases}件のケース}}を進行中に設定しました", "xpack.cases.containers.pushToExternalService": "{ serviceName }への送信が正常に完了しました", "xpack.cases.containers.reopenedCases": "{totalCases, plural, =1 {\"{caseTitle}\"} other {{totalCases}件のケース}}をオープンしました", @@ -9315,7 +9313,6 @@ "xpack.cases.caseTable.changeStatus": "ステータスの変更", "xpack.cases.caseTable.closed": "終了", "xpack.cases.caseTable.closedCases": "終了したケース", - "xpack.cases.caseTable.delete": "削除", "xpack.cases.caseTable.incidentSystem": "インシデント管理システム", "xpack.cases.caseTable.inProgressCases": "進行中のケース", "xpack.cases.caseTable.noCases.body": "ケースを作成するか、フィルターを編集します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ee88a0ed9221..09fb50c164e9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9291,7 +9291,6 @@ "xpack.cases.confirmDeleteCase.confirmQuestion": "删除{quantity, plural, =1 {此案例} other {这些案例}}即会永久移除所有相关案例数据,而且您将无法再将数据推送到外部事件管理系统。是否确定要继续?", "xpack.cases.confirmDeleteCase.deleteCase": "删除{quantity, plural, other {案例}}", "xpack.cases.confirmDeleteCase.deleteTitle": "删除“{caseTitle}”", - "xpack.cases.confirmDeleteCase.selectedCases": "删除“{quantity, plural, =1 {{title}} other {选定的 {quantity} 个案例}}”", "xpack.cases.connecors.get.missingCaseConnectorErrorMessage": "对象类型“{id}”未注册。", "xpack.cases.connecors.register.duplicateCaseConnectorErrorMessage": "已注册对象类型“{id}”。", "xpack.cases.connectors.card.createCommentWarningDesc": "为 {connectorName} 连接器配置“创建注释 URL”和“创建注释对象”字段以在外部共享注释。", @@ -9301,7 +9300,6 @@ "xpack.cases.connectors.cases.externalIncidentUpdated": "(由 {user} 于 {date}更新)", "xpack.cases.connectors.jira.unableToGetIssueMessage": "无法获取 ID 为 {id} 的问题", "xpack.cases.containers.closedCases": "已关闭{totalCases, plural, =1 {“{caseTitle}”} other { {totalCases} 个案例}}", - "xpack.cases.containers.deletedCases": "已删除{totalCases, plural, =1 {“{caseTitle}”} other { {totalCases} 个案例}}", "xpack.cases.containers.markInProgressCases": "已将{totalCases, plural, =1 {“{caseTitle}”} other { {totalCases} 个案例}}标记为进行中", "xpack.cases.containers.pushToExternalService": "已成功发送到 { serviceName }", "xpack.cases.containers.reopenedCases": "已打开{totalCases, plural, =1 {“{caseTitle}”} other { {totalCases} 个案例}}", @@ -9333,7 +9331,6 @@ "xpack.cases.caseTable.changeStatus": "更改状态", "xpack.cases.caseTable.closed": "已关闭", "xpack.cases.caseTable.closedCases": "已关闭案例", - "xpack.cases.caseTable.delete": "删除", "xpack.cases.caseTable.incidentSystem": "事件管理系统", "xpack.cases.caseTable.inProgressCases": "进行中的案例", "xpack.cases.caseTable.noCases.body": "创建案例或编辑筛选。", From 61a300818056c36885874db0d805aa2ab37e24a5 Mon Sep 17 00:00:00 2001 From: Julian Gernun Date: Wed, 28 Sep 2022 19:39:44 +0200 Subject: [PATCH 073/185] add ownfocus flyout prop (#142085) --- .../sections/alerts_table/alerts_flyout/alerts_flyout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx index e57e8c990206..3ddefe1c4cf6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx @@ -99,7 +99,7 @@ export const AlertsFlyout: React.FunctionComponent = ({ ); return ( - + {isLoading && } From 13b283ac4d1d4602e0c700599ef2449d90ee8a23 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 28 Sep 2022 13:46:32 -0400 Subject: [PATCH 074/185] [Fleet] Fix preconfigured ca_trusted_fingerprint in output (#142109) --- .../services/preconfiguration/outputs.test.ts | 27 +++++++++++++++++++ .../services/preconfiguration/outputs.ts | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts index a8eaa39d427d..e6fa2e008e4b 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts @@ -112,6 +112,33 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); + it('should create a preconfigured output with ca_trusted_fingerprint that does not exists', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: false, + is_default_monitoring: false, + hosts: ['http://test.fr'], + ca_trusted_fingerprint: 'testfingerprint', + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.create).toBeCalledWith( + expect.anything(), + expect.objectContaining({ + ca_trusted_fingerprint: 'testfingerprint', + }), + expect.anything() + ); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + it('should create preconfigured logstash output that does not exist', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts index a61d8316dcc5..d7f43ce181a4 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts @@ -78,7 +78,7 @@ export async function createOrUpdatePreconfiguredOutputs( config_yaml: configYaml ?? null, // Set value to null to update these fields on update ca_sha256: outputData.ca_sha256 ?? null, - ca_trusted_fingerprint: outputData.ca_sha256 ?? null, + ca_trusted_fingerprint: outputData.ca_trusted_fingerprint ?? null, ssl: outputData.ssl ?? null, }; From d05eca48e857118ad0ae357d93584051ce62a186 Mon Sep 17 00:00:00 2001 From: doakalexi <109488926+doakalexi@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:24:40 -0400 Subject: [PATCH 075/185] Removing action subgroups (#141794) --- x-pack/plugins/alerting/README.md | 21 +- .../plugins/alerting/common/alert_summary.ts | 1 - .../alerting/server/alert/alert.test.ts | 210 +--------------- x-pack/plugins/alerting/server/alert/alert.ts | 57 +---- .../lib/alert_summary_from_event_log.test.ts | 13 - .../lib/alert_summary_from_event_log.ts | 3 - .../alerting_event_logger.test.ts | 10 +- .../alerting_event_logger.ts | 12 +- ...eate_alert_event_log_record_object.test.ts | 4 - .../create_alert_event_log_record_object.ts | 5 +- .../server/rules_client/rules_client.ts | 3 +- .../server/rules_client/tests/disable.test.ts | 2 - .../tests/get_alert_summary.test.ts | 3 - .../task_runner/create_execution_handler.ts | 3 - .../alerting/server/task_runner/fixtures.ts | 16 +- .../alerting/server/task_runner/log_alerts.ts | 18 +- .../schedule_actions_for_alerts.ts | 23 +- .../server/task_runner/task_runner.test.ts | 95 +------ .../transform_action_params.test.ts | 7 +- .../task_runner/transform_action_params.ts | 4 - .../alerting/server/task_runner/types.ts | 1 - .../routes/rules/preview_rules_route.ts | 1 - .../preview/alert_instance_factory_stub.ts | 10 - .../plugins/alerts/server/alert_types.ts | 22 +- .../group2/tests/alerting/alerts.ts | 76 ------ .../spaces_only/tests/alerting/event_log.ts | 232 ------------------ .../spaces_only/tests/alerting/notify_when.ts | 91 ------- 27 files changed, 42 insertions(+), 901 deletions(-) diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index eed17e563860..ffe17b91b8ae 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -770,25 +770,10 @@ This factory returns an instance of `Alert`. The `Alert` class has the following |Method|Description| |---|---| |getState()|Get the current state of the alert.| -|scheduleActions(actionGroup, context)|Call this to schedule the execution of actions. The actionGroup is a string `id` that relates to the group of alert `actions` to execute and the context will be used for templating purposes. `scheduleActions` or `scheduleActionsWithSubGroup` should only be called once per alert.| -|scheduleActionsWithSubGroup(actionGroup, subgroup, context)|Call this to schedule the execution of actions within a subgroup. The actionGroup is a string `id` that relates to the group of alert `actions` to execute, the `subgroup` is a dynamic string that denotes a subgroup within the actionGroup and the context will be used for templating purposes. `scheduleActions` or `scheduleActionsWithSubGroup` should only be called once per alert.| -|replaceState(state)|Used to replace the current state of the alert. This doesn't work like React, the entire state must be provided. Use this feature as you see fit. The state that is set will persist between rule executions whenever you re-create an alert with the same id. The alert state will be erased when `scheduleActions` or `scheduleActionsWithSubGroup` aren't called during an execution.| +|scheduleActions(actionGroup, context)|Call this to schedule the execution of actions. The actionGroup is a string `id` that relates to the group of alert `actions` to execute and the context will be used for templating purposes. `scheduleActions` should only be called once per alert.| +|replaceState(state)|Used to replace the current state of the alert. This doesn't work like React, the entire state must be provided. Use this feature as you see fit. The state that is set will persist between rule executions whenever you re-create an alert with the same id. The alert state will be erased when `scheduleActions`isn't called during an execution.| |setContext(context)|Call this to set the context for this alert that is used for templating purposes. -### When should I use `scheduleActions` and `scheduleActionsWithSubGroup`? -The `scheduleActions` or `scheduleActionsWithSubGroup` methods are both used to achieve the same thing: schedule actions to be run under a specific action group. -It's important to note that when actions are scheduled for an alert, we check whether the alert was already active in this action group after the previous execution. If it was, then we might throttle the actions (adhering to the user's configuration), as we don't consider this a change in the alert. - -What happens though, if the alert _has_ changed, but they just happen to be in the same action group after this change? This is where subgroups come in. By specifying a subgroup (using the `scheduleActionsWithSubGroup` method), the alert becomes active within the action group, but it will also keep track of the subgroup. -If the subgroup changes, then the framework will treat the alert as if it had been placed in a new action group. It is important to note that we only use the subgroup to denote a change if both the current execution and the previous one specified a subgroup. - -You might wonder, why bother using a subgroup if you can just add a new action group? -Action Groups are static, and have to be define when the rule type is defined. -Action Subgroups are dynamic, and can be defined on the fly. - -This approach enables users to specify actions under specific action groups, but they can't specify actions that are specific to subgroups. -As subgroups fall under action groups, we will schedule the actions specified for the action group, but the subgroup allows the RuleType implementer to reuse the same action group for multiple different active subgroups. - ### When should I use `setContext`? `setContext` is intended to be used for setting context for recovered alerts. While rule type executors make the determination as to which alerts are active for an execution, the Alerting Framework automatically determines which alerts are recovered for an execution. `setContext` empowers rule type executors to provide additional contextual information for these recovered alerts that will be templated into actions. @@ -798,7 +783,7 @@ There needs to be a way to map rule context into action parameters. For this, we When an alert executes, the first argument is the `group` of actions to execute and the second is the context the rule exposes to templates. We iterate through each action parameter attributes recursively and render templates if they are a string. Templates have access to the following "variables": -- `context` - provided by context argument of `.scheduleActions(...)`, `.scheduleActionsWithSubGroup(...)` and `setContext(...)` on an alert. +- `context` - provided by context argument of `.scheduleActions(...)` and `setContext(...)` on an alert. - `state` - the alert's `state` provided by the most recent `replaceState` call on an alert. - `alertId` - the id of the rule - `alertInstanceId` - the alert id diff --git a/x-pack/plugins/alerting/common/alert_summary.ts b/x-pack/plugins/alerting/common/alert_summary.ts index b04ce59eed1b..fc35e3403fe9 100644 --- a/x-pack/plugins/alerting/common/alert_summary.ts +++ b/x-pack/plugins/alerting/common/alert_summary.ts @@ -35,6 +35,5 @@ export interface AlertStatus { status: AlertStatusValues; muted: boolean; actionGroupId?: string; - actionSubgroup?: string; activeStartDate?: string; } diff --git a/x-pack/plugins/alerting/server/alert/alert.test.ts b/x-pack/plugins/alerting/server/alert/alert.test.ts index eae1b18164b0..d9e05e55a67f 100644 --- a/x-pack/plugins/alerting/server/alert/alert.test.ts +++ b/x-pack/plugins/alerting/server/alert/alert.test.ts @@ -81,10 +81,10 @@ describe('isThrottled', () => { }); }); -describe('scheduledActionGroupOrSubgroupHasChanged()', () => { +describe('scheduledActionGroupHasChanged()', () => { test('should be false if no last scheduled and nothing scheduled', () => { const alert = new Alert('1'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false); + expect(alert.scheduledActionGroupHasChanged()).toEqual(false); }); test('should be false if group does not change', () => { @@ -97,54 +97,13 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }, }); alert.scheduleActions('default'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false); - }); - - test('should be false if group and subgroup does not change', () => { - const alert = new Alert('1', { - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'subgroup', - }, - }, - }); - alert.scheduleActionsWithSubGroup('default', 'subgroup'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false); - }); - - test('should be false if group does not change and subgroup goes from undefined to defined', () => { - const alert = new Alert('1', { - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - }, - }, - }); - alert.scheduleActionsWithSubGroup('default', 'subgroup'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false); - }); - - test('should be false if group does not change and subgroup goes from defined to undefined', () => { - const alert = new Alert('1', { - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'subgroup', - }, - }, - }); - alert.scheduleActions('default'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false); + expect(alert.scheduledActionGroupHasChanged()).toEqual(false); }); test('should be true if no last scheduled and has scheduled action', () => { const alert = new Alert('1'); alert.scheduleActions('default'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true); + expect(alert.scheduledActionGroupHasChanged()).toEqual(true); }); test('should be true if group does change', () => { @@ -157,35 +116,7 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }, }); alert.scheduleActions('penguin'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true); - }); - - test('should be true if group does change and subgroup does change', () => { - const alert = new Alert('1', { - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'subgroup', - }, - }, - }); - alert.scheduleActionsWithSubGroup('penguin', 'fish'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true); - }); - - test('should be true if group does not change and subgroup does change', () => { - const alert = new Alert('1', { - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'subgroup', - }, - }, - }); - alert.scheduleActionsWithSubGroup('default', 'fish'); - expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true); + expect(alert.scheduledActionGroupHasChanged()).toEqual(true); }); }); @@ -296,137 +227,6 @@ describe('scheduleActions()', () => { }); }); -describe('scheduleActionsWithSubGroup()', () => { - test('makes hasScheduledActions() return true', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - }, - }, - }); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.hasScheduledActions()).toEqual(true); - }); - - test('makes isThrottled() return true when throttled and subgroup is the same', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'subgroup', - }, - }, - }); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.isThrottled('1m')).toEqual(true); - }); - - test('makes isThrottled() return true when throttled and last schedule had no subgroup', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - }, - }, - }); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.isThrottled('1m')).toEqual(true); - }); - - test('makes isThrottled() return false when throttled and subgroup is the different', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - subgroup: 'prev-subgroup', - }, - }, - }); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.isThrottled('1m')).toEqual(false); - }); - - test('make isThrottled() return false when throttled expired', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: { - lastScheduledActions: { - date: new Date(), - group: 'default', - }, - }, - }); - clock.tick(120000); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.isThrottled('1m')).toEqual(false); - }); - - test('makes getScheduledActionOptions() return given options', () => { - const alert = new Alert('1', { - state: { foo: true }, - meta: {}, - }); - alert - .replaceState({ otherField: true }) - .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(alert.getScheduledActionOptions()).toEqual({ - actionGroup: 'default', - subgroup: 'subgroup', - context: { field: true }, - state: { otherField: true }, - }); - }); - - test('cannot schdule for execution twice', () => { - const alert = new Alert('1'); - alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(() => - alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Alert instance execution has already been scheduled, cannot schedule twice"` - ); - }); - - test('cannot schdule for execution twice with different subgroups', () => { - const alert = new Alert('1'); - alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); - expect(() => - alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Alert instance execution has already been scheduled, cannot schedule twice"` - ); - }); - - test('cannot schdule for execution twice whether there are subgroups', () => { - const alert = new Alert('1'); - alert.scheduleActions('default', { field: true }); - expect(() => - alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Alert instance execution has already been scheduled, cannot schedule twice"` - ); - }); -}); - describe('replaceState()', () => { test('replaces previous state', () => { const alert = new Alert('1', { diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index bf29cacf556c..e24b15de41db 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -23,7 +23,6 @@ interface ScheduledExecutionOptions< ActionGroupIds extends string = DefaultActionGroupId > { actionGroup: ActionGroupIds; - subgroup?: string; context: Context; state: State; } @@ -34,13 +33,7 @@ export type PublicAlert< ActionGroupIds extends string = DefaultActionGroupId > = Pick< Alert, - | 'getState' - | 'replaceState' - | 'scheduleActions' - | 'scheduleActionsWithSubGroup' - | 'setContext' - | 'getContext' - | 'hasContext' + 'getState' | 'replaceState' | 'scheduleActions' | 'setContext' | 'getContext' | 'hasContext' >; export class Alert< @@ -80,10 +73,6 @@ export class Alert< this.meta.lastScheduledActions, this.scheduledExecutionOptions ) && - this.scheduledActionSubgroupIsUnchanged( - this.meta.lastScheduledActions, - this.scheduledExecutionOptions - ) && this.meta.lastScheduledActions.date.getTime() + throttleMills > Date.now() ) { return true; @@ -91,7 +80,7 @@ export class Alert< return false; } - scheduledActionGroupOrSubgroupHasChanged(): boolean { + scheduledActionGroupHasChanged(): boolean { if (!this.meta.lastScheduledActions && this.scheduledExecutionOptions) { // it is considered a change when there are no previous scheduled actions // and new scheduled actions @@ -100,18 +89,11 @@ export class Alert< if (this.meta.lastScheduledActions && this.scheduledExecutionOptions) { // compare previous and new scheduled actions if both exist - return ( - !this.scheduledActionGroupIsUnchanged( - this.meta.lastScheduledActions, - this.scheduledExecutionOptions - ) || - !this.scheduledActionSubgroupIsUnchanged( - this.meta.lastScheduledActions, - this.scheduledExecutionOptions - ) + return !this.scheduledActionGroupIsUnchanged( + this.meta.lastScheduledActions, + this.scheduledExecutionOptions ); } - // no previous and no new scheduled actions return false; } @@ -123,15 +105,6 @@ export class Alert< return lastScheduledActions.group === scheduledExecutionOptions.actionGroup; } - private scheduledActionSubgroupIsUnchanged( - lastScheduledActions: NonNullable, - scheduledExecutionOptions: ScheduledExecutionOptions - ) { - return lastScheduledActions.subgroup && scheduledExecutionOptions.subgroup - ? lastScheduledActions.subgroup === scheduledExecutionOptions.subgroup - : true; - } - getLastScheduledActions() { return this.meta.lastScheduledActions; } @@ -168,22 +141,6 @@ export class Alert< return this; } - scheduleActionsWithSubGroup( - actionGroup: ActionGroupIds, - subgroup: string, - context: Context = {} as Context - ) { - this.ensureHasNoScheduledActions(); - this.setContext(context); - this.scheduledExecutionOptions = { - actionGroup, - subgroup, - context, - state: this.state, - }; - return this; - } - setContext(context: Context) { this.context = context; return this; @@ -200,8 +157,8 @@ export class Alert< return this; } - updateLastScheduledActions(group: ActionGroupIds, subgroup?: string) { - this.meta.lastScheduledActions = { group, subgroup, date: new Date() }; + updateLastScheduledActions(group: ActionGroupIds) { + this.meta.lastScheduledActions = { group, date: new Date() }; } /** diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts index d2040f8e63f3..56a862f2ad6c 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts @@ -121,14 +121,12 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": true, "status": "OK", }, "alert-2": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": true, "status": "OK", @@ -233,7 +231,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "OK", @@ -274,7 +271,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "OK", @@ -314,7 +310,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "OK", @@ -355,7 +350,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": "action group A", - "actionSubgroup": undefined, "activeStartDate": "2020-06-18T00:00:00.000Z", "muted": false, "status": "Active", @@ -396,7 +390,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": "2020-06-18T00:00:00.000Z", "muted": false, "status": "Active", @@ -437,7 +430,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": "action group B", - "actionSubgroup": undefined, "activeStartDate": "2020-06-18T00:00:00.000Z", "muted": false, "status": "Active", @@ -476,7 +468,6 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": "action group A", - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "Active", @@ -519,14 +510,12 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": "action group A", - "actionSubgroup": undefined, "activeStartDate": "2020-06-18T00:00:00.000Z", "muted": true, "status": "Active", }, "alert-2": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": true, "status": "OK", @@ -576,14 +565,12 @@ describe('alertSummaryFromEventLog', () => { "alerts": Object { "alert-1": Object { "actionGroupId": "action group B", - "actionSubgroup": undefined, "activeStartDate": "2020-06-18T00:00:00.000Z", "muted": false, "status": "Active", }, "alert-2": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "OK", diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index 54ac23bf94f2..d8e5f4dea9b4 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -87,14 +87,12 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) case EVENT_LOG_ACTIONS.activeInstance: status.status = 'Active'; status.actionGroupId = event?.kibana?.alerting?.action_group_id; - status.actionSubgroup = event?.kibana?.alerting?.action_subgroup; break; case LEGACY_EVENT_LOG_ACTIONS.resolvedInstance: case EVENT_LOG_ACTIONS.recoveredInstance: status.status = 'OK'; status.activeStartDate = undefined; status.actionGroupId = undefined; - status.actionSubgroup = undefined; } } @@ -153,7 +151,6 @@ function getAlertStatus(alerts: Map, alertId: string): Aler status: 'OK', muted: false, actionGroupId: undefined, - actionSubgroup: undefined, activeStartDate: undefined, }; alerts.set(alertId, status); diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 91904901adcf..8fe081864b11 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -59,9 +59,8 @@ const contextWithName = { ...contextWithScheduleDelay, ruleName: 'my-super-cool- const alert = { action: EVENT_LOG_ACTIONS.activeInstance, id: 'aaabbb', - message: `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup'; actionSubGroup: 'bSubGroup'`, + message: `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup';`, group: 'aGroup', - subgroup: 'bSubgroup', state: { start: '2020-01-01T02:00:00.000Z', end: '2020-01-01T03:00:00.000Z', @@ -74,7 +73,6 @@ const action = { typeId: '.email', alertId: '123', alertGroup: 'aGroup', - alertSubgroup: 'bSubgroup', }; describe('AlertingEventLogger', () => { @@ -1006,14 +1004,13 @@ describe('createAlertRecord', () => { expect(record.event?.end).toEqual(alert.state.end); expect(record.event?.duration).toEqual(alert.state.duration); expect(record.message).toEqual( - `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup'; actionSubGroup: 'bSubGroup'` + `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup';` ); expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); expect(record.kibana?.alerting?.instance_id).toEqual(alert.id); expect(record.kibana?.alerting?.action_group_id).toEqual(alert.group); - expect(record.kibana?.alerting?.action_subgroup).toEqual(alert.subgroup); expect(record.kibana?.saved_objects).toEqual([ { id: contextWithName.ruleId, @@ -1059,14 +1056,13 @@ describe('createActionExecuteRecord', () => { expect(record.event?.kind).toEqual('alert'); expect(record.event?.category).toEqual([contextWithName.ruleType.producer]); expect(record.message).toEqual( - `alert: test:123: 'my-super-cool-rule' instanceId: '123' scheduled actionGroup(subgroup): 'aGroup(bSubgroup)' action: .email:abc` + `alert: test:123: 'my-super-cool-rule' instanceId: '123' scheduled actionGroup: 'aGroup' action: .email:abc` ); expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); expect(record.kibana?.alerting?.instance_id).toEqual(action.alertId); expect(record.kibana?.alerting?.action_group_id).toEqual(action.alertGroup); - expect(record.kibana?.alerting?.action_subgroup).toEqual(action.alertSubgroup); expect(record.kibana?.saved_objects).toEqual([ { id: contextWithName.ruleId, diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 96be872d81b8..18e4044172eb 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -47,7 +47,6 @@ interface AlertOpts { id: string; message: string; group?: string; - subgroup?: string; state?: AlertInstanceState; } @@ -56,7 +55,6 @@ interface ActionOpts { typeId: string; alertId: string; alertGroup?: string; - alertSubgroup?: string; } export class AlertingEventLogger { @@ -232,7 +230,6 @@ export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { state: alert.state, instanceId: alert.id, group: alert.group, - subgroup: alert.subgroup, message: alert.message, savedObjects: [ { @@ -257,14 +254,7 @@ export function createActionExecuteRecord(context: RuleContextOpts, action: Acti action: EVENT_LOG_ACTIONS.executeAction, instanceId: action.alertId, group: action.alertGroup, - subgroup: action.alertSubgroup, - message: `alert: ${context.ruleType.id}:${context.ruleId}: '${context.ruleName}' instanceId: '${ - action.alertId - }' scheduled ${ - action.alertSubgroup - ? `actionGroup(subgroup): '${action.alertGroup}(${action.alertSubgroup})'` - : `actionGroup: '${action.alertGroup}'` - } action: ${action.typeId}:${action.id}`, + message: `alert: ${context.ruleType.id}:${context.ruleId}: '${context.ruleName}' instanceId: '${action.alertId}' scheduled actionGroup: '${action.alertGroup}' action: ${action.typeId}:${action.id}`, savedObjects: [ { id: context.ruleId, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index ba16b7c553e8..8f73ba3a5686 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -99,7 +99,6 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'message text here', namespace: 'default', - subgroup: 'subgroup value', state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -136,7 +135,6 @@ describe('createAlertEventLogRecordObject', () => { }, alerting: { action_group_id: 'group 1', - action_subgroup: 'subgroup value', instance_id: 'test1', }, saved_objects: [ @@ -174,7 +172,6 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'action execution start', namespace: 'default', - subgroup: 'subgroup value', state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -216,7 +213,6 @@ describe('createAlertEventLogRecordObject', () => { }, alerting: { action_group_id: 'group 1', - action_subgroup: 'subgroup value', instance_id: 'test1', }, saved_objects: [ diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index cd7eda500d15..a0f229c0b46d 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -23,7 +23,6 @@ interface CreateAlertEventLogRecordParams { message?: string; state?: AlertInstanceState; group?: string; - subgroup?: string; namespace?: string; timestamp?: string; task?: { @@ -48,18 +47,16 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor task, ruleId, group, - subgroup, namespace, consumer, spaceId, } = params; const alerting = - params.instanceId || group || subgroup + params.instanceId || group ? { alerting: { ...(params.instanceId ? { instance_id: params.instanceId } : {}), ...(group ? { action_group_id: group } : {}), - ...(subgroup ? { action_subgroup: subgroup } : {}), }, } : undefined; diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index f865a0472465..89ce20b59ae9 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -2404,7 +2404,7 @@ export class RulesClient { const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); for (const instanceId of recoveredAlertInstanceIds) { - const { group: actionGroup, subgroup: actionSubgroup } = + const { group: actionGroup } = recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; const instanceState = recoveredAlertInstances[instanceId].getState(); const message = `instance '${instanceId}' has recovered due to the rule was disabled`; @@ -2419,7 +2419,6 @@ export class RulesClient { message, state: instanceState, group: actionGroup, - subgroup: actionSubgroup, namespace: this.namespace, spaceId: this.spaceId, savedObjects: [ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index cca7cd0d7032..558d33ecca87 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -250,7 +250,6 @@ describe('disable()', () => { meta: { lastScheduledActions: { group: 'default', - subgroup: 'newSubgroup', date: new Date().toISOString(), }, }, @@ -319,7 +318,6 @@ describe('disable()', () => { }, alerting: { action_group_id: 'default', - action_subgroup: 'newSubgroup', instance_id: '1', }, saved_objects: [ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 6dcd64565c56..4aa7ae40f878 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -156,21 +156,18 @@ describe('getAlertSummary()', () => { "alerts": Object { "alert-currently-active": Object { "actionGroupId": "action group A", - "actionSubgroup": undefined, "activeStartDate": "2019-02-12T21:01:22.479Z", "muted": false, "status": "Active", }, "alert-muted-no-activity": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": true, "status": "OK", }, "alert-previously-active": Object { "actionGroupId": undefined, - "actionSubgroup": undefined, "activeStartDate": undefined, "muted": false, "status": "OK", diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 7146ef4a5225..51ba1404b2a4 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -65,7 +65,6 @@ export function createExecutionHandler< return async ({ actionGroup, - actionSubgroup, context, state, ruleRunMetricsStore, @@ -92,7 +91,6 @@ export function createExecutionHandler< alertInstanceId: alertId, alertActionGroup: actionGroup, alertActionGroupName: ruleTypeActionGroups.get(actionGroup)!, - alertActionSubgroup: actionSubgroup, context, actionParams: action.params, actionId: action.id, @@ -203,7 +201,6 @@ export function createExecutionHandler< typeId: actionTypeId, alertId, alertGroup: actionGroup, - alertSubgroup: actionSubgroup, }); } diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 39b90efbc786..0a90456dbdad 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -178,7 +178,7 @@ export const mockTaskInstance = () => ({ ownerId: null, }); -export const generateAlertOpts = ({ action, group, subgroup, state, id }: GeneratorParams = {}) => { +export const generateAlertOpts = ({ action, group, state, id }: GeneratorParams = {}) => { id = id ?? '1'; let message: string = ''; switch (action) { @@ -186,9 +186,7 @@ export const generateAlertOpts = ({ action, group, subgroup, state, id }: Genera message = `test:1: 'rule-name' created new alert: '${id}'`; break; case EVENT_LOG_ACTIONS.activeInstance: - message = subgroup - ? `test:1: 'rule-name' active alert: '${id}' in actionGroup(subgroup): 'default(${subgroup})'` - : `test:1: 'rule-name' active alert: '${id}' in actionGroup: 'default'`; + message = `test:1: 'rule-name' active alert: '${id}' in actionGroup: 'default'`; break; case EVENT_LOG_ACTIONS.recoveredInstance: message = `test:1: 'rule-name' alert '${id}' has recovered`; @@ -200,21 +198,14 @@ export const generateAlertOpts = ({ action, group, subgroup, state, id }: Genera message, state, ...(group ? { group } : {}), - ...(subgroup ? { subgroup } : {}), }; }; -export const generateActionOpts = ({ - subgroup, - id, - alertGroup, - alertId, -}: GeneratorParams = {}) => ({ +export const generateActionOpts = ({ id, alertGroup, alertId }: GeneratorParams = {}) => ({ id: id ?? '1', typeId: 'action', alertId: alertId ?? '1', alertGroup: alertGroup ?? 'default', - ...(subgroup ? { alertSubgroup: subgroup } : {}), }); export const generateRunnerResult = ({ @@ -280,7 +271,6 @@ export const generateAlertInstance = ({ id, duration, start }: GeneratorParams = lastScheduledActions: { date: new Date(DATE_1970), group: 'default', - subgroup: undefined, }, }, state: { diff --git a/x-pack/plugins/alerting/server/task_runner/log_alerts.ts b/x-pack/plugins/alerting/server/task_runner/log_alerts.ts index 87da1fb67f33..2abe72ed06cb 100644 --- a/x-pack/plugins/alerting/server/task_runner/log_alerts.ts +++ b/x-pack/plugins/alerting/server/task_runner/log_alerts.ts @@ -92,8 +92,7 @@ export function logAlerts< ruleRunMetricsStore.setNumberOfRecoveredAlerts(recoveredAlertIds.length); for (const id of recoveredAlertIds) { - const { group: actionGroup, subgroup: actionSubgroup } = - recoveredAlerts[id].getLastScheduledActions() ?? {}; + const { group: actionGroup } = recoveredAlerts[id].getLastScheduledActions() ?? {}; const state = recoveredAlerts[id].getState(); const message = `${ruleLogPrefix} alert '${id}' has recovered`; @@ -101,41 +100,32 @@ export function logAlerts< action: EVENT_LOG_ACTIONS.recoveredInstance, id, group: actionGroup, - subgroup: actionSubgroup, message, state, }); } for (const id of newAlertIds) { - const { actionGroup, subgroup: actionSubgroup } = - activeAlerts[id].getScheduledActionOptions() ?? {}; + const { actionGroup } = activeAlerts[id].getScheduledActionOptions() ?? {}; const state = activeAlerts[id].getState(); const message = `${ruleLogPrefix} created new alert: '${id}'`; alertingEventLogger.logAlert({ action: EVENT_LOG_ACTIONS.newInstance, id, group: actionGroup, - subgroup: actionSubgroup, message, state, }); } for (const id of activeAlertIds) { - const { actionGroup, subgroup: actionSubgroup } = - activeAlerts[id].getScheduledActionOptions() ?? {}; + const { actionGroup } = activeAlerts[id].getScheduledActionOptions() ?? {}; const state = activeAlerts[id].getState(); - const message = `${ruleLogPrefix} active alert: '${id}' in ${ - actionSubgroup - ? `actionGroup(subgroup): '${actionGroup}(${actionSubgroup})'` - : `actionGroup: '${actionGroup}'` - }`; + const message = `${ruleLogPrefix} active alert: '${id}' in actionGroup: '${actionGroup}'`; alertingEventLogger.logAlert({ action: EVENT_LOG_ACTIONS.activeInstance, id, group: actionGroup, - subgroup: actionSubgroup, message, state, }); diff --git a/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts index e0cc6d5b81c3..8c1d62ecf4e3 100644 --- a/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts +++ b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts @@ -49,16 +49,8 @@ export async function scheduleActionsForAlerts< notifyWhen ); if (executeAction && alert.hasScheduledActions()) { - const { actionGroup, subgroup: actionSubgroup, state } = alert.getScheduledActionOptions()!; - await executeAlert( - alertId, - alert, - executionHandler, - ruleRunMetricsStore, - actionGroup, - state, - actionSubgroup - ); + const { actionGroup, state } = alert.getScheduledActionOptions()!; + await executeAlert(alertId, alert, executionHandler, ruleRunMetricsStore, actionGroup, state); } } @@ -94,14 +86,12 @@ async function executeAlert< executionHandler: ExecutionHandler, ruleRunMetricsStore: RuleRunMetricsStore, actionGroup: ActionGroupIds | RecoveryActionGroupId, - state: InstanceState, - actionSubgroup?: string + state: InstanceState ) { - alert.updateLastScheduledActions(actionGroup, actionSubgroup); + alert.updateLastScheduledActions(actionGroup); alert.unscheduleActions(); return executionHandler({ actionGroup, - actionSubgroup, context: alert.getContext(), state, alertId, @@ -133,10 +123,7 @@ function shouldExecuteAction< muted ? 'muted' : 'throttled' }` ); - } else if ( - notifyWhen === 'onActionGroupChange' && - !alert.scheduledActionGroupOrSubgroupHasChanged() - ) { + } else if (notifyWhen === 'onActionGroupChange' && !alert.scheduledActionGroupHasChanged()) { executeAction = false; logger.debug( `skipping scheduling of actions for '${alertId}' in rule ${ruleLabel}: alert is active but action group has not changed` diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 4ce85f54c3dc..a199fb3b5599 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -312,9 +312,7 @@ describe('Task Runner', () => { AlertInstanceContext, string >) => { - executorServices.alertFactory - .create('1') - .scheduleActionsWithSubGroup('default', 'subDefault'); + executorServices.alertFactory.create('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( @@ -360,7 +358,6 @@ describe('Task Runner', () => { generateAlertOpts({ action: EVENT_LOG_ACTIONS.newInstance, group: 'default', - subgroup: 'subDefault', state: { start: DATE_1970, duration: '0' }, }) ); @@ -369,14 +366,10 @@ describe('Task Runner', () => { generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, group: 'default', - subgroup: 'subDefault', state: { start: DATE_1970, duration: '0' }, }) ); - expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( - 1, - generateActionOpts({ subgroup: 'subDefault' }) - ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts()); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } @@ -859,90 +852,6 @@ describe('Task Runner', () => { } ); - test.each(ephemeralTestParams)( - 'actionsPlugin.execute is called when notifyWhen=onActionGroupChange and alert state subgroup has changed %s', - async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => { - customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue( - true - ); - - customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue( - true - ); - ruleType.executor.mockImplementation( - async ({ - services: executorServices, - }: RuleExecutorOptions< - RuleTypeParams, - RuleTypeState, - AlertInstanceState, - AlertInstanceContext, - string - >) => { - executorServices.alertFactory - .create('1') - .scheduleActionsWithSubGroup('default', 'subgroup1'); - } - ); - const taskRunner = new TaskRunner( - ruleType, - { - ...mockedTaskInstance, - state: { - ...mockedTaskInstance.state, - alertInstances: { - '1': { - meta: { - lastScheduledActions: { - group: 'default', - subgroup: 'newSubgroup', - date: new Date().toISOString(), - }, - }, - state: { bar: false }, - }, - }, - }, - }, - customTaskRunnerFactoryInitializerParams, - inMemoryMetrics - ); - expect(AlertingEventLogger).toHaveBeenCalled(); - - rulesClient.get.mockResolvedValue({ - ...mockedRuleTypeSavedObject, - notifyWhen: 'onActionGroupChange', - }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); - await taskRunner.run(); - - testAlertingEventLogCalls({ - activeAlerts: 1, - triggeredActions: 1, - generatedActions: 1, - status: 'active', - logAlert: 1, - logAction: 1, - }); - expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( - 1, - generateAlertOpts({ - action: EVENT_LOG_ACTIONS.activeInstance, - state: { bar: false }, - group: 'default', - subgroup: 'subgroup1', - }) - ); - expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( - 1, - generateActionOpts({ subgroup: 'subgroup1' }) - ); - - expect(enqueueFunction).toHaveBeenCalledTimes(1); - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - } - ); - test.each(ephemeralTestParams)( 'includes the apiKey in the request used to initialize the actionsClient %s', async ( diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts index 6235e630ba0b..ababe16dea37 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts @@ -417,7 +417,6 @@ test('rule variables are passed to templates', () => { alertInstanceId: '2', alertActionGroup: 'action-group', alertActionGroupName: 'Action Group', - alertActionSubgroup: 'subgroup', alertParams: {}, }); expect(result).toMatchInlineSnapshot(` @@ -429,8 +428,7 @@ test('rule variables are passed to templates', () => { test('rule alert variables are passed to templates', () => { const actionParams = { - message: - 'Value "{{alert.id}}", "{{alert.actionGroup}}", "{{alert.actionGroupName}}" and "{{alert.actionSubgroup}}" exist', + message: 'Value "{{alert.id}}", "{{alert.actionGroup}}" and "{{alert.actionGroupName}}" exist', }; const result = transformActionParams({ actionsPlugin, @@ -447,12 +445,11 @@ test('rule alert variables are passed to templates', () => { alertInstanceId: '2', alertActionGroup: 'action-group', alertActionGroupName: 'Action Group', - alertActionSubgroup: 'subgroup', alertParams: {}, }); expect(result).toMatchInlineSnapshot(` Object { - "message": "Value \\"2\\", \\"action-group\\", \\"Action Group\\" and \\"subgroup\\" exist", + "message": "Value \\"2\\", \\"action-group\\" and \\"Action Group\\" exist", } `); }); diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts index 322a16c1c238..61ec54c48886 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts @@ -25,7 +25,6 @@ interface TransformActionParamsOptions { alertInstanceId: string; alertActionGroup: string; alertActionGroupName: string; - alertActionSubgroup?: string; actionParams: RuleActionParams; alertParams: RuleTypeParams; state: AlertInstanceState; @@ -44,7 +43,6 @@ export function transformActionParams({ tags, alertInstanceId, alertActionGroup, - alertActionSubgroup, alertActionGroupName, context, actionParams, @@ -63,7 +61,6 @@ export function transformActionParams({ alertInstanceId, alertActionGroup, alertActionGroupName, - alertActionSubgroup, context, date: new Date().toISOString(), state, @@ -80,7 +77,6 @@ export function transformActionParams({ id: alertInstanceId, actionGroup: alertActionGroup, actionGroupName: alertActionGroupName, - actionSubgroup: alertActionSubgroup, }, }; return actionsPlugin.renderActionParameterTemplates( diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts index 87bd65b02848..ce439fa3b3b0 100644 --- a/x-pack/plugins/alerting/server/task_runner/types.ts +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -116,7 +116,6 @@ export interface CreateExecutionHandlerOptions< export interface ExecutionHandlerOptions { actionGroup: ActionGroupIds; - actionSubgroup?: string; alertId: string; context: AlertInstanceContext; state: AlertInstanceState; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index a147118b782d..15b6ffe47d34 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -182,7 +182,6 @@ export const previewRulesRoute = async ( | 'getState' | 'replaceState' | 'scheduleActions' - | 'scheduleActionsWithSubGroup' | 'setContext' | 'getContext' | 'hasContext' diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts index b73119d15077..a3cf4ebc95d7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts @@ -37,16 +37,6 @@ export const alertInstanceFactoryStub = < meta: { lastScheduledActions: { group: 'default', date: new Date() } }, }); }, - scheduleActionsWithSubGroup( - actionGroup: TActionGroupIds, - subgroup: string, - alertcontext: TInstanceContext - ) { - return new Alert('', { - state: {} as TInstanceState, - meta: { lastScheduledActions: { group: 'default', date: new Date() } }, - }); - }, setContext(alertContext: TInstanceContext) { return new Alert('', { state: {} as TInstanceState, diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index 3d68c420e444..068c51661861 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -109,31 +109,21 @@ async function alwaysFiringExecutor(alertExecutorOptions: any) { rule, } = alertExecutorOptions; let group: string | null = 'default'; - let subgroup: string | null = null; const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy, ...rule }; if (params.groupsToScheduleActionsInSeries) { const index = state.groupInSeriesIndex || 0; - const [scheduledGroup, scheduledSubgroup] = ( - params.groupsToScheduleActionsInSeries[index] ?? '' - ).split(':'); + const [scheduledGroup] = (params.groupsToScheduleActionsInSeries[index] ?? '').split(':'); group = scheduledGroup; - subgroup = scheduledSubgroup; } if (group) { const instance = services.alertFactory.create('1').replaceState({ instanceStateValue: true }); - if (subgroup) { - instance.scheduleActionsWithSubGroup(group, subgroup, { - instanceContextValue: true, - }); - } else { - instance.scheduleActions(group, { - instanceContextValue: true, - }); - } + instance.scheduleActions(group, { + instanceContextValue: true, + }); } await services.scopedClusterClient.asCurrentUser.index({ @@ -506,9 +496,7 @@ function getPatternFiringAlertType() { deep: DeepContextVariables, }); } else if (typeof scheduleByPattern === 'string') { - services.alertFactory - .create(instanceId) - .scheduleActionsWithSubGroup('default', scheduleByPattern); + services.alertFactory.create(instanceId).scheduleActions('default', scheduleByPattern); } } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts index 63c70e93ea19..8f1b8047e433 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts @@ -953,82 +953,6 @@ instanceStateValue: true } }); - it('should not throttle when changing subgroups', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - schedule: { interval: '1s' }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - groupsToScheduleActionsInSeries: ['default:prev', 'default:next'], - }, - actions: [ - { - group: 'default', - id: indexRecordActionId, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: 'from:{{alertActionGroup}}:{{alertActionSubgroup}}', - }, - }, - ], - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForDisabled(response.body.id, testStart); - - // Ensure only 2 actions with proper params exists - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(2); - const messages: string[] = searchResult.body.hits.hits.map( - // @ts-expect-error _source: unknown - (hit: { _source: { params: { message: string } } }) => hit._source.params.message - ); - expect(messages.sort()).to.eql(['from:default:next', 'from:default:prev']); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - it('should reset throttle window when not firing', async () => { const testStart = new Date(); const reference = alertUtils.generateReference(); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts index 206036ef7fca..2e63bed19786 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import uuid from 'uuid'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { Spaces } from '../../scenarios'; import { @@ -447,237 +446,6 @@ export default function eventLogTests({ getService }: FtrProviderContext) { } }); - it('should generate expected events for normal operation with subgroups', 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); - - // pattern of when the alert should fire - const [firstSubgroup, secondSubgroup] = [uuid.v4(), uuid.v4()]; - const pattern = { - instance: [false, firstSubgroup, secondSubgroup], - }; - - const response = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, - throttle: null, - params: { - pattern, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: {}, - }, - ], - }) - ); - - expect(response.status).to.eql(200); - const alertId = response.body.id; - objectRemover.add(space.id, alertId, 'rule', 'alerting'); - - // get the events we're expecting - const events = await retry.try(async () => { - return await getEventLog({ - getService, - spaceId: space.id, - type: 'alert', - id: alertId, - provider: 'alerting', - actions: new Map([ - // make sure the counts of the # of events per type are as expected - ['execute-start', { gte: 4 }], - ['execute', { gte: 4 }], - ['execute-action', { equal: 2 }], - ['new-instance', { equal: 1 }], - ['active-instance', { gte: 2 }], - ['recovered-instance', { equal: 1 }], - ]), - }); - }); - - const executeEvents = getEventsByAction(events, 'execute'); - const executeStartEvents = getEventsByAction(events, 'execute-start'); - const newInstanceEvents = getEventsByAction(events, 'new-instance'); - const recoveredInstanceEvents = getEventsByAction(events, 'recovered-instance'); - - // make sure the events are in the right temporal order - const executeTimes = getTimestamps(executeEvents); - const executeStartTimes = getTimestamps(executeStartEvents); - const newInstanceTimes = getTimestamps(newInstanceEvents); - const recoveredInstanceTimes = getTimestamps(recoveredInstanceEvents); - - expect(executeTimes[0] < newInstanceTimes[0]).to.be(true); - expect(executeTimes[1] >= newInstanceTimes[0]).to.be(true); - expect(executeTimes[2] > newInstanceTimes[0]).to.be(true); - expect(executeStartTimes.length === executeTimes.length).to.be(true); - expect(recoveredInstanceTimes[0] > newInstanceTimes[0]).to.be(true); - - // validate each event - let executeCount = 0; - let numActiveAlerts = 0; - let numNewAlerts = 0; - let numRecoveredAlerts = 0; - let currentExecutionId; - const executeStatuses = ['ok', 'active', 'active']; - for (const event of events) { - switch (event?.event?.action) { - case 'execute-start': - currentExecutionId = event?.kibana?.alert?.rule?.execution?.uuid; - validateEvent(event, { - spaceId: space.id, - savedObjects: [ - { type: 'alert', id: alertId, rel: 'primary', type_id: 'test.patternFiring' }, - ], - message: `rule execution start: "${alertId}"`, - shouldHaveTask: true, - executionId: currentExecutionId, - ruleTypeId: response.body.rule_type_id, - rule: { - id: alertId, - category: response.body.rule_type_id, - license: 'basic', - ruleset: 'alertsFixture', - }, - consumer: 'alertsFixture', - }); - break; - case 'execute-action': - expect( - [firstSubgroup, secondSubgroup].includes( - event?.kibana?.alerting?.action_subgroup! - ) - ).to.be(true); - validateEvent(event, { - spaceId: space.id, - savedObjects: [ - { type: 'alert', id: alertId, rel: 'primary', type_id: 'test.patternFiring' }, - { type: 'action', id: createdAction.id, type_id: 'test.noop' }, - ], - message: `alert: test.patternFiring:${alertId}: 'abc' instanceId: 'instance' scheduled actionGroup(subgroup): 'default(${event?.kibana?.alerting?.action_subgroup})' action: test.noop:${createdAction.id}`, - instanceId: 'instance', - actionGroupId: 'default', - executionId: currentExecutionId, - ruleTypeId: response.body.rule_type_id, - rule: { - id: alertId, - category: response.body.rule_type_id, - license: 'basic', - ruleset: 'alertsFixture', - name: response.body.name, - }, - consumer: 'alertsFixture', - }); - break; - case 'new-instance': - numNewAlerts++; - validateInstanceEvent( - event, - `created new alert: 'instance'`, - false, - currentExecutionId - ); - break; - case 'recovered-instance': - numRecoveredAlerts++; - validateInstanceEvent( - event, - `alert 'instance' has recovered`, - true, - currentExecutionId - ); - break; - case 'active-instance': - numActiveAlerts++; - expect( - [firstSubgroup, secondSubgroup].includes( - event?.kibana?.alerting?.action_subgroup! - ) - ).to.be(true); - validateInstanceEvent( - event, - `active alert: 'instance' in actionGroup(subgroup): 'default(${event?.kibana?.alerting?.action_subgroup})'`, - false, - currentExecutionId - ); - break; - case 'execute': - validateEvent(event, { - spaceId: space.id, - savedObjects: [ - { type: 'alert', id: alertId, rel: 'primary', type_id: 'test.patternFiring' }, - ], - outcome: 'success', - message: `rule executed: test.patternFiring:${alertId}: 'abc'`, - status: executeStatuses[executeCount++], - shouldHaveTask: true, - executionId: currentExecutionId, - ruleTypeId: response.body.rule_type_id, - rule: { - id: alertId, - category: response.body.rule_type_id, - license: 'basic', - ruleset: 'alertsFixture', - name: response.body.name, - }, - consumer: 'alertsFixture', - numActiveAlerts, - numNewAlerts, - numRecoveredAlerts, - }); - numActiveAlerts = 0; - numNewAlerts = 0; - numRecoveredAlerts = 0; - break; - // this will get triggered as we add new event actions - default: - throw new Error(`unexpected event action "${event?.event?.action}"`); - } - } - - function validateInstanceEvent( - event: IValidatedEvent, - subMessage: string, - shouldHaveEventEnd: boolean, - executionId?: string - ) { - validateEvent(event, { - spaceId: space.id, - savedObjects: [ - { type: 'alert', id: alertId, rel: 'primary', type_id: 'test.patternFiring' }, - ], - message: `test.patternFiring:${alertId}: 'abc' ${subMessage}`, - instanceId: 'instance', - actionGroupId: 'default', - shouldHaveEventEnd, - executionId, - ruleTypeId: response.body.rule_type_id, - rule: { - id: alertId, - category: response.body.rule_type_id, - license: 'basic', - ruleset: 'alertsFixture', - name: response.body.name, - }, - consumer: 'alertsFixture', - }); - } - }); - it('should generate events for execution errors', async () => { const response = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts index 5024a1489f4c..a7996f19554e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts @@ -174,97 +174,6 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext ); expect(executeActionEventsActionGroup).to.eql(expectedActionGroupBasedOnPattern); }); - - it(`alert with notifyWhen=onActionGroupChange should only execute actions when action subgroup changes`, async () => { - const { body: defaultAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My Default Action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - const { body: recoveredAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My Recovered Action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - const pattern = { - instance: [ - 'subgroup1', - 'subgroup1', - false, - false, - 'subgroup1', - 'subgroup2', - 'subgroup2', - false, - ], - }; - const expectedActionGroupBasedOnPattern = [ - 'default', - 'recovered', - 'default', - 'default', - 'recovered', - ]; - - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.patternFiring', - params: { pattern }, - schedule: { interval: '1s' }, - throttle: null, - notify_when: 'onActionGroupChange', - actions: [ - { - id: defaultAction.id, - group: 'default', - params: {}, - }, - { - id: recoveredAction.id, - group: 'recovered', - params: {}, - }, - ], - }) - ) - .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const events = await retry.try(async () => { - return await getEventLog({ - getService, - spaceId: Spaces.space1.id, - type: 'alert', - id: createdAlert.id, - provider: 'alerting', - actions: new Map([ - ['execute-action', { gte: 5 }], - ['new-instance', { equal: 2 }], - ]), - }); - }); - - const executeActionEvents = getEventsByAction(events, 'execute-action'); - const executeActionEventsActionGroup = executeActionEvents.map( - (event) => event?.kibana?.alerting?.action_group_id - ); - expect(executeActionEventsActionGroup).to.eql(expectedActionGroupBasedOnPattern); - }); }); } From 77867e162f1eb2a377865605196515c1e1b4ea45 Mon Sep 17 00:00:00 2001 From: Wafaa Nasr Date: Wed, 28 Sep 2022 20:36:05 +0200 Subject: [PATCH 076/185] Exceptions List component (#140985) * add components with a draft exception-list-details to test * fix jest config in xPack=> security=> Public * fix tests * fix header test and use RTL * covert meta test to use RTL and header * fix utlity messageid * fix messageid in utilty * create osCondition, entryContent and entryContent.helper from Conditions.tsx * comment test until fixing all * create package with first components + test + jest config * add constants for url * [CI] Auto-commit changed files from 'node scripts/generate codeowners' * disable tests until finishing moving rest of components or fix it+ add securityLinkAnchorComponent temp; * add exceptionList-components +fixning build issues * add exceptionList-components +fixning build issues * fix translations id + pass comments as props * move utiitly out of package until moving all + fixing css * copy non-js/ts files through babel * remove list-details-components * apply comments * apply comments in references * fix meta tests * update tests + add some descriptions * fix camelcase file name in Readme * fix camelcase file name in Readme Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: spalger --- .github/CODEOWNERS | 1 + .i18nrc.json | 1 + package.json | 2 + packages/BUILD.bazel | 2 + .../BUILD.bazel | 163 ++++++++ .../README.md | 27 ++ .../index.ts | 16 + .../jest.config.js | 21 ++ .../kibana.jsonc | 7 + .../package.json | 8 + .../setup_test.ts | 9 + ...on_product_no_results_magnifying_glass.svg | 1 + .../src/custom.d.ts | 13 + .../empty_viewer_state.test.tsx | 138 +++++++ .../empty_viewer_state/empty_viewer_state.tsx | 131 +++++++ .../exception_item_card/comments/comments.tsx | 45 +++ .../conditions/conditions.config.ts | 34 ++ .../conditions/conditions.styles.tsx | 36 ++ .../conditions/conditions.test.tsx | 350 ++++++++++++++++++ .../conditions/conditions.tsx | 56 +++ .../entry_content/entry_content.helper.tsx | 48 +++ .../entry_content/entry_content.tsx | 71 ++++ .../os_conditions/os_conditions.tsx | 34 ++ .../exception_item_card/conditions/types.ts | 32 ++ .../exception_item_card.tsx | 136 +++++++ .../header/header.test.tsx | 76 ++++ .../src/exception_item_card/header/header.tsx | 83 +++++ .../src/exception_item_card/index.test.tsx | 172 +++++++++ .../src/exception_item_card/index.ts | 13 + .../meta/details_info/details_info.tsx | 59 +++ .../exception_item_card/meta/meta.test.tsx | 155 ++++++++ .../src/exception_item_card/meta/meta.tsx | 123 ++++++ .../src/exception_item_card/translations.ts | 166 +++++++++ .../exception_items/exception_items.test.tsx | 103 ++++++ .../src/exception_items/exception_items.tsx | 139 +++++++ .../src/pagination/pagination.test.tsx | 78 ++++ .../src/pagination/pagination.tsx | 50 +++ .../src/pagination/use_pagination.test.ts | 67 ++++ .../src/pagination/use_pagination.ts | 55 +++ .../src/search_bar/search_bar.test.tsx | 90 +++++ .../src/search_bar/search_bar.tsx | 118 ++++++ .../src/translations.ts | 57 +++ .../src/types/index.ts | 65 ++++ .../src/value_with_space_warning/index.ts | 9 + .../use_value_with_space_warning.test.ts | 54 +++ .../use_value_with_space_warning.ts | 32 ++ .../value_with_space_warning.test.tsx | 48 +++ .../value_with_space_warning.tsx | 47 +++ .../tsconfig.json | 21 ++ .../security_solution/public/jest.config.js | 12 +- yarn.lock | 8 + 51 files changed, 3281 insertions(+), 1 deletion(-) create mode 100644 packages/kbn-securitysolution-exception-list-components/BUILD.bazel create mode 100644 packages/kbn-securitysolution-exception-list-components/README.md create mode 100644 packages/kbn-securitysolution-exception-list-components/index.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/jest.config.js create mode 100644 packages/kbn-securitysolution-exception-list-components/kibana.jsonc create mode 100644 packages/kbn-securitysolution-exception-list-components/package.json create mode 100644 packages/kbn-securitysolution-exception-list-components/setup_test.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg create mode 100644 packages/kbn-securitysolution-exception-list-components/src/custom.d.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/translations.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/types/index.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx create mode 100644 packages/kbn-securitysolution-exception-list-components/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d802eb4024a0..0a1fcc60d55b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -915,6 +915,7 @@ packages/kbn-rule-data-utils @elastic/apm-ui packages/kbn-safer-lodash-set @elastic/kibana-security packages/kbn-securitysolution-autocomplete @elastic/security-solution-platform packages/kbn-securitysolution-es-utils @elastic/security-solution-platform +packages/kbn-securitysolution-exception-list-components @elastic/security-solution-platform packages/kbn-securitysolution-hook-utils @elastic/security-solution-platform packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-solution-platform packages/kbn-securitysolution-io-ts-list-types @elastic/security-solution-platform diff --git a/.i18nrc.json b/.i18nrc.json index 20b2588ca2fa..462daff20de6 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -60,6 +60,7 @@ "kibana-react": "src/plugins/kibana_react", "kibanaOverview": "src/plugins/kibana_overview", "lists": "packages/kbn-securitysolution-list-utils/src", + "exceptionList-components": "packages/kbn-securitysolution-exception-list-components/src", "management": ["src/legacy/core_plugins/management", "src/plugins/management"], "monaco": "packages/kbn-monaco/src", "navigation": "src/plugins/navigation", diff --git a/package.json b/package.json index ebbff20d9173..eedb8db4b605 100644 --- a/package.json +++ b/package.json @@ -335,6 +335,7 @@ "@kbn/safer-lodash-set": "link:bazel-bin/packages/kbn-safer-lodash-set", "@kbn/securitysolution-autocomplete": "link:bazel-bin/packages/kbn-securitysolution-autocomplete", "@kbn/securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils", + "@kbn/securitysolution-exception-list-components": "link:bazel-bin/packages/kbn-securitysolution-exception-list-components", "@kbn/securitysolution-hook-utils": "link:bazel-bin/packages/kbn-securitysolution-hook-utils", "@kbn/securitysolution-io-ts-alerting-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types", "@kbn/securitysolution-io-ts-list-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-list-types", @@ -1086,6 +1087,7 @@ "@types/kbn__rule-data-utils": "link:bazel-bin/packages/kbn-rule-data-utils/npm_module_types", "@types/kbn__securitysolution-autocomplete": "link:bazel-bin/packages/kbn-securitysolution-autocomplete/npm_module_types", "@types/kbn__securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils/npm_module_types", + "@types/kbn__securitysolution-exception-list-components": "link:bazel-bin/packages/kbn-securitysolution-exception-list-components/npm_module_types", "@types/kbn__securitysolution-hook-utils": "link:bazel-bin/packages/kbn-securitysolution-hook-utils/npm_module_types", "@types/kbn__securitysolution-io-ts-alerting-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types/npm_module_types", "@types/kbn__securitysolution-io-ts-list-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-list-types/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 455b851a4936..cf97e501df09 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -251,6 +251,7 @@ filegroup( "//packages/kbn-safer-lodash-set:build", "//packages/kbn-securitysolution-autocomplete:build", "//packages/kbn-securitysolution-es-utils:build", + "//packages/kbn-securitysolution-exception-list-components:build", "//packages/kbn-securitysolution-hook-utils:build", "//packages/kbn-securitysolution-io-ts-alerting-types:build", "//packages/kbn-securitysolution-io-ts-list-types:build", @@ -574,6 +575,7 @@ filegroup( "//packages/kbn-safer-lodash-set:build_types", "//packages/kbn-securitysolution-autocomplete:build_types", "//packages/kbn-securitysolution-es-utils:build_types", + "//packages/kbn-securitysolution-exception-list-components:build_types", "//packages/kbn-securitysolution-hook-utils:build_types", "//packages/kbn-securitysolution-io-ts-alerting-types:build_types", "//packages/kbn-securitysolution-io-ts-list-types:build_types", diff --git a/packages/kbn-securitysolution-exception-list-components/BUILD.bazel b/packages/kbn-securitysolution-exception-list-components/BUILD.bazel new file mode 100644 index 000000000000..6436793fa5f3 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/BUILD.bazel @@ -0,0 +1,163 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + + +PKG_DIRNAME = "kbn-securitysolution-exception-list-components" +PKG_REQUIRE_NAME = "@kbn/securitysolution-exception-list-components" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + "**/*.svg", + "**/*.d.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "jest.config.js" +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//react", + "//packages/kbn-securitysolution-io-ts-list-types", + "//packages/kbn-securitysolution-autocomplete", + "//packages/kbn-ui-theme", + "//packages/kbn-i18n-react", + "//packages/kbn-i18n", + "@npm//@elastic/eui", + "@npm//@emotion/css", + "@npm//@emotion/react", + "@npm//@testing-library/jest-dom", + "@npm//jest", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "//packages/kbn-securitysolution-io-ts-list-types:npm_module_types", + "//packages/kbn-securitysolution-autocomplete:npm_module_types", + "//packages/kbn-ui-theme:npm_module_types", + "//packages/kbn-i18n-react:npm_module_types", + "@npm//@elastic/eui", + "@npm//@emotion/css", + "@npm//@emotion/react", + "@npm//jest", + +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), + additional_args = [ + "--copy-files" + ], +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-securitysolution-exception-list-components/README.md b/packages/kbn-securitysolution-exception-list-components/README.md new file mode 100644 index 000000000000..e23b85e40996 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/README.md @@ -0,0 +1,27 @@ +# @kbn/securitysolution-exception-list-components + +This is where the building UI components of the Exception-List live +Most of the components here are imported from `x-pack/plugins/security_solutions/public/detection_engine` + +# Aim + +TODO + +# Pattern used + +``` +component + index.tsx + index.styles.ts <-- to hold styles if the component has many custom styles + use_component.ts <-- for logic if the Presentational Component has logic + index.test.tsx + use_component.test.tsx +``` + +# Next + +- Now the `ExceptionItems, ExceptionItemCard +and ExceptionItemCardMetaInfo + ` receive `securityLinkAnchorComponent, exceptionsUtilityComponent +, and exceptionsUtilityComponent +` as props to avoid moving all the `common` components under the `x-pack` at once, later we should move all building blocks to this `kbn-package` diff --git a/packages/kbn-securitysolution-exception-list-components/index.ts b/packages/kbn-securitysolution-exception-list-components/index.ts new file mode 100644 index 000000000000..f5001ff35fd3 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './src/search_bar/search_bar'; +export * from './src/empty_viewer_state/empty_viewer_state'; +export * from './src/pagination/pagination'; +// export * from './src/exceptions_utility/exceptions_utility'; +export * from './src/exception_items/exception_items'; +export * from './src/exception_item_card'; +export * from './src/value_with_space_warning'; +export * from './src/types'; diff --git a/packages/kbn-securitysolution-exception-list-components/jest.config.js b/packages/kbn-securitysolution-exception-list-components/jest.config.js new file mode 100644 index 000000000000..37a11c23c75b --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/jest.config.js @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-exception-list-components'], + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/packages/kbn-securitysolution-exception-list-components/**/*.{ts,tsx}', + '!/packages/kbn-securitysolution-exception-list-components/**/*.test', + ], + setupFilesAfterEnv: [ + '/packages/kbn-securitysolution-exception-list-components/setup_test.ts', + ], +}; diff --git a/packages/kbn-securitysolution-exception-list-components/kibana.jsonc b/packages/kbn-securitysolution-exception-list-components/kibana.jsonc new file mode 100644 index 000000000000..081c50d35af0 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-exception-list-components", + "owner": "@elastic/security-solution-platform", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/kbn-securitysolution-exception-list-components/package.json b/packages/kbn-securitysolution-exception-list-components/package.json new file mode 100644 index 000000000000..263863d725c1 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-exception-list-components", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-securitysolution-exception-list-components/setup_test.ts b/packages/kbn-securitysolution-exception-list-components/setup_test.ts new file mode 100644 index 000000000000..bb55d97ec930 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/setup_test.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +// eslint-disable-next-line import/no-extraneous-dependencies +import '@testing-library/jest-dom'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg b/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg new file mode 100644 index 000000000000..b9a0df1630b2 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts b/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts new file mode 100644 index 000000000000..9169166fe7af --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +declare module '*.svg' { + const content: string; + // eslint-disable-next-line import/no-default-export + export default content; +} diff --git a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx new file mode 100644 index 000000000000..43943e0e8fb9 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; + +import { EmptyViewerState } from './empty_viewer_state'; +import { ListTypeText, ViewerStatus } from '../types'; + +describe('EmptyViewerState', () => { + it('it should render "error" with the default title and body', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('errorViewerState')).toBeTruthy(); + expect(wrapper.getByTestId('errorTitle')).toHaveTextContent('Unable to load exception items'); + expect(wrapper.getByTestId('errorBody')).toHaveTextContent( + 'There was an error loading the exception items. Contact your administrator for help.' + ); + }); + it('it should render "error" when sending the title and body props', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('errorViewerState')).toBeTruthy(); + expect(wrapper.getByTestId('errorTitle')).toHaveTextContent('Error title'); + expect(wrapper.getByTestId('errorBody')).toHaveTextContent('Error body'); + }); + it('it should render loading', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('loadingViewerState')).toBeTruthy(); + }); + it('it should render empty search with the default title and body', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('emptySearchViewerState')).toBeTruthy(); + expect(wrapper.getByTestId('emptySearchTitle')).toHaveTextContent( + 'No results match your search criteria' + ); + expect(wrapper.getByTestId('emptySearchBody')).toHaveTextContent('Try modifying your search'); + }); + it('it should render empty search when sending title and body props', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('emptySearchViewerState')).toBeTruthy(); + expect(wrapper.getByTestId('emptySearchTitle')).toHaveTextContent('Empty search title'); + expect(wrapper.getByTestId('emptySearchBody')).toHaveTextContent('Empty search body'); + }); + it('it should render no items screen when sending title and body props', () => { + const wrapper = render( + + ); + + const { getByTestId } = wrapper; + expect(getByTestId('emptyBody')).toHaveTextContent('There are no endpoint exceptions.'); + expect(getByTestId('emptyStateButton')).toHaveTextContent('Add endpoint exception'); + expect(getByTestId('emptyViewerState')).toBeTruthy(); + }); + it('it should render no items with default title and body props', () => { + const wrapper = render( + + ); + + const { getByTestId } = wrapper; + expect(getByTestId('emptyViewerState')).toBeTruthy(); + expect(getByTestId('emptyTitle')).toHaveTextContent('Add exceptions to this rule'); + expect(getByTestId('emptyBody')).toHaveTextContent( + 'There is no exception in your rule. Create your first rule exception.' + ); + expect(getByTestId('emptyStateButton')).toHaveTextContent('Create rule exception'); + }); + it('it should render no items screen with default title and body props and listType endPoint', () => { + const wrapper = render( + + ); + + const { getByTestId } = wrapper; + expect(getByTestId('emptyViewerState')).toBeTruthy(); + expect(getByTestId('emptyTitle')).toHaveTextContent('Add exceptions to this rule'); + expect(getByTestId('emptyBody')).toHaveTextContent( + 'There is no exception in your rule. Create your first rule exception.' + ); + expect(getByTestId('emptyStateButton')).toHaveTextContent('Create endpoint exception'); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.tsx b/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.tsx new file mode 100644 index 000000000000..060d2ecc1506 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo } from 'react'; +import type { FC } from 'react'; +import { css } from '@emotion/react'; +import { + EuiLoadingContent, + EuiImage, + EuiEmptyPrompt, + EuiButton, + useEuiTheme, + EuiPanel, +} from '@elastic/eui'; +import type { ExpressionColor } from '@elastic/eui/src/components/expression/expression'; +import type { EuiFacetGroupLayout } from '@elastic/eui/src/components/facet/facet_group'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { ListTypeText, ViewerStatus } from '../types'; +import * as i18n from '../translations'; +import illustration from '../assets/images/illustration_product_no_results_magnifying_glass.svg'; + +interface EmptyViewerStateProps { + title?: string; + body?: string; + buttonText?: string; + listType?: ListTypeText; + isReadOnly: boolean; + viewerStatus: ViewerStatus; + onCreateExceptionListItem?: () => void | null; +} + +const panelCss = css` + margin: ${euiThemeVars.euiSizeL} 0; + padding: ${euiThemeVars.euiSizeL} 0; +`; +const EmptyViewerStateComponent: FC = ({ + title, + body, + buttonText, + listType, + isReadOnly, + viewerStatus, + onCreateExceptionListItem, +}) => { + const { euiTheme } = useEuiTheme(); + + const euiEmptyPromptProps = useMemo(() => { + switch (viewerStatus) { + case ViewerStatus.ERROR: { + return { + color: 'danger' as ExpressionColor, + iconType: 'alert', + title: ( +

    {title || i18n.EMPTY_VIEWER_STATE_ERROR_TITLE}

    + ), + body:

    {body || i18n.EMPTY_VIEWER_STATE_ERROR_BODY}

    , + 'data-test-subj': 'errorViewerState', + }; + } + case ViewerStatus.EMPTY: + return { + color: 'subdued' as ExpressionColor, + iconType: 'plusInCircle', + iconColor: euiTheme.colors.darkestShade, + title: ( +

    {title || i18n.EMPTY_VIEWER_STATE_EMPTY_TITLE}

    + ), + body:

    {body || i18n.EMPTY_VIEWER_STATE_EMPTY_BODY}

    , + 'data-test-subj': 'emptyViewerState', + actions: [ + + {buttonText || i18n.EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON(listType || 'rule')} + , + ], + }; + case ViewerStatus.EMPTY_SEARCH: + return { + color: 'plain' as ExpressionColor, + layout: 'horizontal' as EuiFacetGroupLayout, + hasBorder: true, + hasShadow: false, + icon: , + title: ( +

    + {title || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE} +

    + ), + body: ( +

    + {body || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY} +

    + ), + 'data-test-subj': 'emptySearchViewerState', + }; + } + }, [ + viewerStatus, + euiTheme.colors.darkestShade, + title, + body, + onCreateExceptionListItem, + isReadOnly, + buttonText, + listType, + ]); + + if (viewerStatus === ViewerStatus.LOADING || viewerStatus === ViewerStatus.SEARCHING) + return ; + + return ( + + + + ); +}; + +export const EmptyViewerState = React.memo(EmptyViewerStateComponent); + +EmptyViewerState.displayName = 'EmptyViewerState'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx new file mode 100644 index 000000000000..ca08d10f0a04 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo } from 'react'; +import type { EuiCommentProps } from '@elastic/eui'; +import { EuiAccordion, EuiCommentList, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as i18n from '../translations'; + +const accordionCss = css` + color: ${euiThemeVars.euiColorPrimary}; +`; + +export interface ExceptionItemCardCommentsProps { + comments: EuiCommentProps[]; +} + +export const ExceptionItemCardComments = memo(({ comments }) => { + return ( + + + {i18n.exceptionItemCardCommentsAccordion(comments.length)} + + } + arrowDisplay="none" + data-test-subj="exceptionsViewerCommentAccordion" + > + + + + + + ); +}); + +ExceptionItemCardComments.displayName = 'ExceptionItemCardComments'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts new file mode 100644 index 000000000000..08514a64feac --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from '../translations'; + +export const OS_LABELS = Object.freeze({ + linux: i18n.OS_LINUX, + mac: i18n.OS_MAC, + macos: i18n.OS_MAC, + windows: i18n.OS_WINDOWS, +}); + +export const OPERATOR_TYPE_LABELS_INCLUDED = Object.freeze({ + [ListOperatorTypeEnum.NESTED]: i18n.CONDITION_OPERATOR_TYPE_NESTED, + [ListOperatorTypeEnum.MATCH_ANY]: i18n.CONDITION_OPERATOR_TYPE_MATCH_ANY, + [ListOperatorTypeEnum.MATCH]: i18n.CONDITION_OPERATOR_TYPE_MATCH, + [ListOperatorTypeEnum.WILDCARD]: i18n.CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES, + [ListOperatorTypeEnum.EXISTS]: i18n.CONDITION_OPERATOR_TYPE_EXISTS, + [ListOperatorTypeEnum.LIST]: i18n.CONDITION_OPERATOR_TYPE_LIST, +}); + +export const OPERATOR_TYPE_LABELS_EXCLUDED = Object.freeze({ + [ListOperatorTypeEnum.MATCH_ANY]: i18n.CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY, + [ListOperatorTypeEnum.MATCH]: i18n.CONDITION_OPERATOR_TYPE_NOT_MATCH, + [ListOperatorTypeEnum.WILDCARD]: i18n.CONDITION_OPERATOR_TYPE_WILDCARD_DOES_NOT_MATCH, + [ListOperatorTypeEnum.EXISTS]: i18n.CONDITION_OPERATOR_TYPE_DOES_NOT_EXIST, + [ListOperatorTypeEnum.LIST]: i18n.CONDITION_OPERATOR_TYPE_NOT_IN_LIST, +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx new file mode 100644 index 000000000000..3ad2d7ef21fb --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { cx } from '@emotion/css'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; + +// TODO check font Roboto Mono +export const nestedGroupSpaceCss = css` + margin-left: ${euiThemeVars.euiSizeXL}; + margin-bottom: ${euiThemeVars.euiSizeXS}; + padding-top: ${euiThemeVars.euiSizeXS}; +`; + +export const borderCss = cx( + 'eui-xScroll', + ` + border: 1px; + border-color: #d3dae6; + border-style: solid; +` +); + +export const valueContainerCss = css` + display: flex; + align-items: center; + margin-left: ${euiThemeVars.euiSizeS}; +`; +export const expressionContainerCss = css` + display: flex; + align-items: center; +`; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx new file mode 100644 index 000000000000..ae4b76a4a7dc --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx @@ -0,0 +1,350 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; + +import { ExceptionItemCardConditions } from './conditions'; + +interface TestEntry { + field: string; + operator: 'included' | 'excluded'; + type: unknown; + value?: string | string[]; + entries?: TestEntry[]; + list?: { id: string; type: string }; +} +const getEntryKey = ( + entry: TestEntry, + index: string, + list?: { id: string; type: string } | null +) => { + if (list && Object.keys(list)) { + const { field, type, list: entryList } = entry; + const { id } = entryList || {}; + return `${field}${type}${id || ''}${index}`; + } + const { field, type, value } = entry; + return `${field}${type}${value || ''}${index}`; +}; + +describe('ExceptionItemCardConditions', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetAllMocks(); + }); + it('it includes os condition if one exists', () => { + const entries: TestEntry[] = [ + { + field: 'host.name', + operator: 'included', + type: 'match', + value: 'host', + }, + { + field: 'threat.indicator.port', + operator: 'included', + type: 'exists', + }, + { + entries: [ + { + field: 'valid', + operator: 'included', + type: 'match', + value: 'true', + }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + operator: 'included', + }, + ]; + const wrapper = render( + + ); + expect(wrapper.getByTestId('exceptionItemConditionsOs')).toHaveTextContent('OSIS Linux'); + + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[0], '0')}EntryContent`) + ).toHaveTextContent('host.nameIS host'); + + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[1], '1')}EntryContent`) + ).toHaveTextContent('AND threat.indicator.portexists'); + + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[2], '2')}EntryContent`) + ).toHaveTextContent('AND file.Ext.code_signature'); + + if (entries[2] && entries[2].entries) { + expect( + wrapper.getByTestId( + `exceptionItemConditions${getEntryKey(entries[2].entries[0], '0')}EntryContent` + ) + ).toHaveTextContent('validIS true'); + } + }); + + it('it renders item conditions', () => { + const entries: TestEntry[] = [ + { + field: 'host.name', + operator: 'included', + type: 'match', + value: 'host', + }, + { + field: 'host.name', + operator: 'excluded', + type: 'match', + value: 'host', + }, + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['foo', 'bar'], + }, + { + field: 'host.name', + operator: 'excluded', + type: 'match_any', + value: ['foo', 'bar'], + }, + { + field: 'user.name', + operator: 'included', + type: 'wildcard', + value: 'foo*', + }, + { + field: 'user.name', + operator: 'excluded', + type: 'wildcard', + value: 'foo*', + }, + { + field: 'threat.indicator.port', + operator: 'included', + type: 'exists', + }, + { + field: 'threat.indicator.port', + operator: 'excluded', + type: 'exists', + }, + { + entries: [ + { + field: 'valid', + operator: 'included', + type: 'match', + value: 'true', + }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + operator: 'included', + }, + ]; + const wrapper = render( + + ); + expect(wrapper.queryByTestId('exceptionItemConditionsOs')).not.toBeInTheDocument(); + + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[0], '0')}EntryContent`) + ).toHaveTextContent('host.nameIS host'); + // Match; + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[1], '1')}EntryContent`) + ).toHaveTextContent('AND host.nameIS NOT host'); + // MATCH_ANY; + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[2], '2')}EntryContent`) + ).toHaveTextContent('AND host.nameis one of foobar'); + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[3], '3')}EntryContent`) + ).toHaveTextContent('AND host.nameis not one of foobar'); + // WILDCARD; + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[4], '4')}EntryContent`) + ).toHaveTextContent('AND user.nameMATCHES foo*'); + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[5], '5')}EntryContent`) + ).toHaveTextContent('AND user.nameDOES NOT MATCH foo*'); + // EXISTS; + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[6], '6')}EntryContent`) + ).toHaveTextContent('AND threat.indicator.portexists'); + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[7], '7')}EntryContent`) + ).toHaveTextContent('AND threat.indicator.portdoes not exist'); + // NESTED; + expect( + wrapper.getByTestId(`exceptionItemConditions${getEntryKey(entries[8], '8')}EntryContent`) + ).toHaveTextContent('AND file.Ext.code_signature'); + if (entries[8] && entries[8].entries) { + expect( + wrapper.getByTestId( + `exceptionItemConditions${getEntryKey(entries[8].entries[0], '0')}EntryContent` + ) + ).toHaveTextContent('validIS true'); + } + }); + it('it renders list conditions', () => { + const entries: TestEntry[] = [ + { + field: 'host.name', + list: { + id: 'ips.txt', + type: 'keyword', + }, + operator: 'included', + type: 'list', + }, + { + field: 'host.name', + list: { + id: 'ips.txt', + type: 'keyword', + }, + operator: 'excluded', + type: 'list', + }, + ]; + const wrapper = render( + + ); + // /exceptionItemConditionshost.namelist0EntryContent + expect( + wrapper.getByTestId( + `exceptionItemConditions${getEntryKey(entries[0], '0', entries[0].list)}EntryContent` + ) + ).toHaveTextContent('host.nameincluded in ips.txt'); + + expect( + wrapper.getByTestId( + `exceptionItemConditions${getEntryKey(entries[1], '1', entries[1].list)}EntryContent` + ) + ).toHaveTextContent('AND host.nameis not included in ips.txt'); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.tsx new file mode 100644 index 000000000000..8b85a7343afc --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo } from 'react'; +import { EuiPanel } from '@elastic/eui'; + +import { borderCss } from './conditions.styles'; +import { EntryContent } from './entry_content/entry_content'; +import { OsCondition } from './os_conditions/os_conditions'; +import type { CriteriaConditionsProps, Entry } from './types'; + +export const ExceptionItemCardConditions = memo( + ({ os, entries, dataTestSubj }) => { + return ( + + {os?.length ? : null} + {entries.map((entry: Entry, index: number) => { + const nestedEntries = 'entries' in entry ? entry.entries : []; + return ( +
    + + {nestedEntries?.length + ? nestedEntries.map((nestedEntry: Entry, nestedIndex: number) => ( + + )) + : null} +
    + ); + })} +
    + ); + } +); +ExceptionItemCardConditions.displayName = 'ExceptionItemCardConditions'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx new file mode 100644 index 000000000000..6a64bcc810c0 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiExpression, EuiBadge } from '@elastic/eui'; +import type { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { ValueWithSpaceWarning } from '../../../..'; +import { OPERATOR_TYPE_LABELS_EXCLUDED, OPERATOR_TYPE_LABELS_INCLUDED } from '../conditions.config'; +import type { Entry } from '../types'; + +const getEntryValue = (type: string, value?: string | string[]) => { + if (type === 'match_any' && Array.isArray(value)) { + return value.map((currentValue) => {currentValue}); + } + return value ?? ''; +}; + +export const getEntryOperator = (type: ListOperatorTypeEnum, operator: string) => { + if (type === 'nested') return ''; + return operator === 'included' + ? OPERATOR_TYPE_LABELS_INCLUDED[type] ?? type + : OPERATOR_TYPE_LABELS_EXCLUDED[type] ?? type; +}; + +export const getValue = (entry: Entry) => { + if (entry.type === 'list') return entry.list.id; + + return 'value' in entry ? entry.value : ''; +}; + +export const getValueExpression = ( + type: ListOperatorTypeEnum, + operator: string, + value: string | string[] +) => ( + <> + + + +); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.tsx new file mode 100644 index 000000000000..6c321a6d0ce0 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo } from 'react'; +import { EuiExpression, EuiToken, EuiFlexGroup } from '@elastic/eui'; +import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { + nestedGroupSpaceCss, + valueContainerCss, + expressionContainerCss, +} from '../conditions.styles'; +import type { Entry } from '../types'; +import * as i18n from '../../translations'; +import { getValue, getValueExpression } from './entry_content.helper'; + +export const EntryContent = memo( + ({ + entry, + index, + isNestedEntry = false, + dataTestSubj, + }: { + entry: Entry; + index: number; + isNestedEntry?: boolean; + dataTestSubj?: string; + }) => { + const { field, type } = entry; + const value = getValue(entry); + const operator = 'operator' in entry ? entry.operator : ''; + + const entryKey = `${field}${type}${value}${index}`; + return ( +
    +
    + {isNestedEntry ? ( + + + +
    + + {getValueExpression(type as ListOperatorTypeEnum, operator, value)} +
    +
    + ) : ( + <> + + + {getValueExpression(type as ListOperatorTypeEnum, operator, value)} + + )} +
    +
    + ); + } +); +EntryContent.displayName = 'EntryContent'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.tsx new file mode 100644 index 000000000000..701529ae6717 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo, useMemo } from 'react'; +import { EuiExpression } from '@elastic/eui'; + +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { OS_LABELS } from '../conditions.config'; +import * as i18n from '../../translations'; + +export interface OsConditionsProps { + dataTestSubj: string; + os: ExceptionListItemSchema['os_types']; +} + +export const OsCondition = memo(({ os, dataTestSubj }) => { + const osLabel = useMemo(() => { + return os.map((osValue) => OS_LABELS[osValue] ?? osValue).join(', '); + }, [os]); + return osLabel ? ( +
    + + + + +
    + ) : null; +}); +OsCondition.displayName = 'OsCondition'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts new file mode 100644 index 000000000000..09067e84cafc --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { + EntryExists, + EntryList, + EntryMatch, + EntryMatchAny, + EntryMatchWildcard, + EntryNested, + ExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; + +export type Entry = + | EntryExists + | EntryList + | EntryMatch + | EntryMatchAny + | EntryMatchWildcard + | EntryNested; + +export type Entries = ExceptionListItemSchema['entries']; +export interface CriteriaConditionsProps { + entries: Entries; + dataTestSubj: string; + os?: ExceptionListItemSchema['os_types']; +} diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx new file mode 100644 index 000000000000..c3705750d015 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo, useCallback, FC } from 'react'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiCommentProps } from '@elastic/eui'; +import type { + CommentsArray, + ExceptionListItemSchema, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; + +import * as i18n from './translations'; +import { + ExceptionItemCardHeader, + ExceptionItemCardConditions, + ExceptionItemCardMetaInfo, + ExceptionItemCardComments, +} from '.'; + +import type { ExceptionListItemIdentifiers } from '../types'; + +export interface ExceptionItemProps { + dataTestSubj?: string; + disableActions?: boolean; + exceptionItem: ExceptionListItemSchema; + listType: ExceptionListTypeEnum; + ruleReferences: any[]; // rulereferences + editActionLabel?: string; + deleteActionLabel?: string; + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + formattedDateComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + getFormattedComments: (comments: CommentsArray) => EuiCommentProps[]; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + onDeleteException: (arg: ExceptionListItemIdentifiers) => void; + onEditException: (item: ExceptionListItemSchema) => void; +} + +const ExceptionItemCardComponent: FC = ({ + disableActions = false, + exceptionItem, + listType, + ruleReferences, + dataTestSubj, + editActionLabel, + deleteActionLabel, + securityLinkAnchorComponent, + formattedDateComponent, + getFormattedComments, + onDeleteException, + onEditException, +}) => { + const handleDelete = useCallback((): void => { + onDeleteException({ + id: exceptionItem.id, + name: exceptionItem.name, + namespaceType: exceptionItem.namespace_type, + }); + }, [onDeleteException, exceptionItem.id, exceptionItem.name, exceptionItem.namespace_type]); + + const handleEdit = useCallback((): void => { + onEditException(exceptionItem); + }, [onEditException, exceptionItem]); + + const formattedComments = useMemo((): EuiCommentProps[] => { + return getFormattedComments(exceptionItem.comments); + }, [exceptionItem.comments, getFormattedComments]); + + const actions: Array<{ + key: string; + icon: string; + label: string | boolean; + onClick: () => void; + }> = useMemo( + () => [ + { + key: 'edit', + icon: 'controlsHorizontal', + label: editActionLabel || i18n.exceptionItemCardEditButton(listType), + onClick: handleEdit, + }, + { + key: 'delete', + icon: 'trash', + label: deleteActionLabel || listType === i18n.exceptionItemCardDeleteButton(listType), + onClick: handleDelete, + }, + ], + [editActionLabel, listType, deleteActionLabel, handleDelete, handleEdit] + ); + return ( + + + + + + + + + + + + {formattedComments.length > 0 && ( + + )} + + + ); +}; + +ExceptionItemCardComponent.displayName = 'ExceptionItemCardComponent'; + +export const ExceptionItemCard = React.memo(ExceptionItemCardComponent); + +ExceptionItemCard.displayName = 'ExceptionItemCard'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx new file mode 100644 index 000000000000..78feab598c14 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; + +import * as i18n from '../translations'; +import { ExceptionItemCardHeader } from './header'; +import { fireEvent, render } from '@testing-library/react'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +const handleEdit = jest.fn(); +const handleDelete = jest.fn(); +const actions = [ + { + key: 'edit', + icon: 'pencil', + label: i18n.exceptionItemCardEditButton(ExceptionListTypeEnum.DETECTION), + onClick: handleEdit, + }, + { + key: 'delete', + icon: 'trash', + label: i18n.exceptionItemCardDeleteButton(ExceptionListTypeEnum.DETECTION), + onClick: handleDelete, + }, +]; +describe('ExceptionItemCardHeader', () => { + it('it renders item name', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('exceptionItemHeaderTitle')).toHaveTextContent('some name'); + }); + + it('it displays actions', () => { + const wrapper = render( + + ); + + // click on popover + fireEvent.click(wrapper.getByTestId('exceptionItemHeaderActionButton')); + fireEvent.click(wrapper.getByTestId('exceptionItemHeaderActionItemedit')); + expect(handleEdit).toHaveBeenCalled(); + + fireEvent.click(wrapper.getByTestId('exceptionItemHeaderActionItemdelete')); + expect(handleDelete).toHaveBeenCalled(); + }); + + it('it disables actions if disableActions is true', () => { + const wrapper = render( + + ); + + expect(wrapper.getByTestId('exceptionItemHeaderActionButton')).toBeDisabled(); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx new file mode 100644 index 000000000000..d58cb8d99b7a --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo, useMemo, useState } from 'react'; +import type { EuiContextMenuPanelProps } from '@elastic/eui'; +import { + EuiButtonIcon, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiTitle, + EuiContextMenuItem, +} from '@elastic/eui'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export interface ExceptionItemCardHeaderProps { + item: ExceptionListItemSchema; + actions: Array<{ key: string; icon: string; label: string | boolean; onClick: () => void }>; + disableActions?: boolean; + dataTestSubj: string; +} + +export const ExceptionItemCardHeader = memo( + ({ item, actions, disableActions = false, dataTestSubj }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onItemActionsClick = () => setIsPopoverOpen((isOpen) => !isOpen); + const onClosePopover = () => setIsPopoverOpen(false); + + const itemActions = useMemo((): EuiContextMenuPanelProps['items'] => { + return actions.map((action) => ( + { + onClosePopover(); + action.onClick(); + }} + > + {action.label} + + )); + }, [dataTestSubj, actions]); + + return ( + + + +

    {item.name}

    +
    +
    + + + } + panelPaddingSize="none" + isOpen={isPopoverOpen} + closePopover={onClosePopover} + data-test-subj={`${dataTestSubj}Items`} + > + + + +
    + ); + } +); + +ExceptionItemCardHeader.displayName = 'ExceptionItemCardHeader'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx new file mode 100644 index 000000000000..e97b03607bb6 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; + +import { ExceptionItemCard } from '.'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +const ruleReferences: unknown[] = [ + { + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + }, +]; +describe('ExceptionItemCard', () => { + it('it renders header, item meta information and conditions', () => { + const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: [] }; + + const wrapper = render( + null} + formattedDateComponent={() => null} + getFormattedComments={() => []} + /> + ); + + expect(wrapper.getByTestId('exceptionItemCardHeaderContainer')).toBeInTheDocument(); + // expect(wrapper.getByTestId('exceptionItemCardMetaInfo')).toBeInTheDocument(); + expect(wrapper.getByTestId('exceptionItemCardConditions')).toBeInTheDocument(); + // expect(wrapper.queryByTestId('exceptionsViewerCommentAccordion')).not.toBeInTheDocument(); + }); + + it('it renders header, item meta information, conditions, and comments if any exist', () => { + const exceptionItem = { ...getExceptionListItemSchemaMock(), comments: getCommentsArrayMock() }; + + const wrapper = render( + null} + formattedDateComponent={() => null} + getFormattedComments={() => []} + /> + ); + + expect(wrapper.getByTestId('exceptionItemCardHeaderContainer')).toBeInTheDocument(); + // expect(wrapper.getByTestId('exceptionItemCardMetaInfo')).toBeInTheDocument(); + expect(wrapper.getByTestId('exceptionItemCardConditions')).toBeInTheDocument(); + // expect(wrapper.getByTestId('exceptionsViewerCommentAccordion')).toBeInTheDocument(); + }); + + it('it does not render edit or delete action buttons when "disableActions" is "true"', () => { + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = render( + null} + formattedDateComponent={() => null} + getFormattedComments={() => []} + /> + ); + expect(wrapper.queryByTestId('itemActionButton')).not.toBeInTheDocument(); + }); + + it('it invokes "onEditException" when edit button clicked', () => { + const mockOnEditException = jest.fn(); + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = render( + null} + formattedDateComponent={() => null} + getFormattedComments={() => []} + /> + ); + + fireEvent.click(wrapper.getByTestId('exceptionItemCardHeaderActionButton')); + fireEvent.click(wrapper.getByTestId('exceptionItemCardHeaderActionItemedit')); + expect(mockOnEditException).toHaveBeenCalledWith(getExceptionListItemSchemaMock()); + }); + + it('it invokes "onDeleteException" when delete button clicked', () => { + const mockOnDeleteException = jest.fn(); + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = render( + null} + formattedDateComponent={() => null} + getFormattedComments={() => []} + /> + ); + fireEvent.click(wrapper.getByTestId('exceptionItemCardHeaderActionButton')); + fireEvent.click(wrapper.getByTestId('exceptionItemCardHeaderActionItemdelete')); + + expect(mockOnDeleteException).toHaveBeenCalledWith({ + id: '1', + name: 'some name', + namespaceType: 'single', + }); + }); + + // TODO Fix this Test + // it('it renders comment accordion closed to begin with', () => { + // const exceptionItem = getExceptionListItemSchemaMock(); + // exceptionItem.comments = getCommentsArrayMock(); + // const wrapper = render( + // + // ); + + // expect(wrapper.queryByTestId('accordion-comment-list')).not.toBeVisible(); + // }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts new file mode 100644 index 000000000000..c0fd3fafc86d --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './conditions/conditions'; +export * from './header/header'; +export * from './meta/meta'; +export * from './comments/comments'; +export * from './exception_item_card'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.tsx new file mode 100644 index 000000000000..3d075f50096d --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo } from 'react'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as i18n from '../../translations'; + +interface MetaInfoDetailsProps { + fieldName: string; + label: string; + lastUpdate: JSX.Element | string; + lastUpdateValue: string; + dataTestSubj: string; +} + +const euiBadgeFontFamily = css` + font-family: ${euiThemeVars.euiFontFamily}; +`; +export const MetaInfoDetails = memo( + ({ label, lastUpdate, lastUpdateValue, dataTestSubj }) => { + return ( + + + + {label} + + + + + {lastUpdate} + + + + + {i18n.EXCEPTION_ITEM_CARD_META_BY} + + + + + + + {lastUpdateValue} + + + + + + ); + } +); + +MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx new file mode 100644 index 000000000000..14bdef771d6b --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; + +import { ExceptionItemCardMetaInfo } from './meta'; +import { RuleReference } from '../../types'; + +const ruleReferences = [ + { + exception_lists: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + }, +]; +describe('ExceptionItemCardMetaInfo', () => { + it('it should render creation info with sending custom formattedDateComponent', () => { + const wrapper = render( + null} + formattedDateComponent={({ fieldName, value }) => ( + <> +

    {new Date(value).toDateString()}

    + + )} + /> + ); + + expect(wrapper.getByTestId('exceptionItemMetaCreatedBylastUpdate')).toHaveTextContent( + 'Mon Apr 20 2020' + ); + expect(wrapper.getByTestId('exceptionItemMetaCreatedBylastUpdateValue')).toHaveTextContent( + 'some user' + ); + }); + + it('it should render udate info with sending custom formattedDateComponent', () => { + const wrapper = render( + null} + formattedDateComponent={({ fieldName, value }) => ( + <> +

    {new Date(value).toDateString()}

    + + )} + /> + ); + expect(wrapper.getByTestId('exceptionItemMetaUpdatedBylastUpdate')).toHaveTextContent( + 'Mon Apr 20 2020' + ); + expect(wrapper.getByTestId('exceptionItemMetaUpdatedBylastUpdateValue')).toHaveTextContent( + 'some user' + ); + }); + + it('it should render references info', () => { + const wrapper = render( + null} + formattedDateComponent={() => null} + /> + ); + + expect(wrapper.getByTestId('exceptionItemMetaAffectedRulesButton')).toHaveTextContent( + 'Affects 1 rule' + ); + }); + + it('it renders references info when multiple references exist', () => { + const wrapper = render( + null} + formattedDateComponent={() => null} + /> + ); + + expect(wrapper.getByTestId('exceptionItemMetaAffectedRulesButton')).toHaveTextContent( + 'Affects 2 rules' + ); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx new file mode 100644 index 000000000000..91e0a9cdd19b --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo, useMemo, useState } from 'react'; +import type { EuiContextMenuPanelProps } from '@elastic/eui'; +import { + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, + EuiButtonEmpty, + EuiPopover, +} from '@elastic/eui'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { css } from '@emotion/react'; +import * as i18n from '../translations'; +import type { RuleReference } from '../../types'; +import { MetaInfoDetails } from './details_info/details_info'; + +const itemCss = css` + border-right: 1px solid #d3dae6; + padding: 4px 12px 4px 0; +`; + +export interface ExceptionItemCardMetaInfoProps { + item: ExceptionListItemSchema; + references: RuleReference[]; + dataTestSubj: string; + formattedDateComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common +} + +export const ExceptionItemCardMetaInfo = memo( + ({ item, references, dataTestSubj, securityLinkAnchorComponent, formattedDateComponent }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onAffectedRulesClick = () => setIsPopoverOpen((isOpen) => !isOpen); + const onClosePopover = () => setIsPopoverOpen(false); + + const FormattedDateComponent = formattedDateComponent; + const itemActions = useMemo((): EuiContextMenuPanelProps['items'] => { + if (references == null || securityLinkAnchorComponent === null) { + return []; + } + + const SecurityLinkAnchor = securityLinkAnchorComponent; + return references.map((reference) => ( + + + + + + )); + }, [references, securityLinkAnchorComponent, dataTestSubj]); + + return ( + + {FormattedDateComponent !== null && ( + <> + + + } + lastUpdateValue={item.created_by} + dataTestSubj={`${dataTestSubj}CreatedBy`} + /> + + + + + } + lastUpdateValue={item.updated_by} + dataTestSubj={`${dataTestSubj}UpdatedBy`} + /> + + + )} + + + {i18n.AFFECTED_RULES(references.length)} + + } + panelPaddingSize="none" + isOpen={isPopoverOpen} + closePopover={onClosePopover} + data-test-subj={`${dataTestSubj}Items`} + > + + + + + ); + } +); +ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts new file mode 100644 index 000000000000..2fa752429102 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const exceptionItemCardEditButton = (listType: string) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.editItemButton', { + values: { listType }, + defaultMessage: 'Edit {listType} exception', + }); + +export const exceptionItemCardDeleteButton = (listType: string) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.deleteItemButton', { + values: { listType }, + defaultMessage: 'Delete {listType} exception', + }); + +export const EXCEPTION_ITEM_CARD_CREATED_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.createdLabel', + { + defaultMessage: 'Created', + } +); + +export const EXCEPTION_ITEM_CARD_UPDATED_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.updatedLabel', + { + defaultMessage: 'Updated', + } +); + +export const EXCEPTION_ITEM_CARD_META_BY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.metaDetailsBy', + { + defaultMessage: 'by', + } +); + +export const exceptionItemCardCommentsAccordion = (comments: number) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.showCommentsLabel', { + values: { comments }, + defaultMessage: 'Show {comments, plural, =1 {comment} other {comments}} ({comments})', + }); + +export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator', + { + defaultMessage: 'IS', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator.not', + { + defaultMessage: 'IS NOT', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardMatchesOperator', + { + defaultMessage: 'MATCHES', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD_DOES_NOT_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardDoesNotMatchOperator', + { + defaultMessage: 'DOES NOT MATCH', + } +); + +export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.nestedOperator', + { + defaultMessage: 'has', + } +); + +export const CONDITION_OPERATOR_TYPE_MATCH_ANY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator', + { + defaultMessage: 'is one of', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator.not', + { + defaultMessage: 'is not one of', + } +); + +export const CONDITION_OPERATOR_TYPE_EXISTS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator', + { + defaultMessage: 'exists', + } +); + +export const CONDITION_OPERATOR_TYPE_DOES_NOT_EXIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator.not', + { + defaultMessage: 'does not exist', + } +); + +export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator', + { + defaultMessage: 'included in', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_IN_LIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator.not', + { + defaultMessage: 'is not included in', + } +); + +export const CONDITION_AND = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.and', + { + defaultMessage: 'AND', + } +); + +export const CONDITION_OS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.os', + { + defaultMessage: 'OS', + } +); + +export const OS_WINDOWS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.windows', + { + defaultMessage: 'Windows', + } +); + +export const OS_LINUX = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.linux', + { + defaultMessage: 'Linux', + } +); + +export const OS_MAC = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.macos', + { + defaultMessage: 'Mac', + } +); + +export const AFFECTED_RULES = (numRules: number) => + i18n.translate('exceptionList-components.exceptions.card.exceptionItem.affectedRules', { + values: { numRules }, + defaultMessage: 'Affects {numRules} {numRules, plural, =1 {rule} other {rules}}', + }); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx new file mode 100644 index 000000000000..3fe2d7eb6d0b --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { ExceptionItems } from './exception_items'; + +import { ViewerStatus } from '../types'; +import { render } from '@testing-library/react'; + +const onCreateExceptionListItem = jest.fn(); +const onDeleteException = jest.fn(); +const onEditExceptionItem = jest.fn(); +const onPaginationChange = jest.fn(); + +const pagination = { pageIndex: 0, pageSize: 0, totalItemCount: 0 }; + +describe('ExceptionsViewerItems', () => { + describe('Viewing EmptyViewerState', () => { + it('it renders empty prompt if "viewerStatus" is "empty"', () => { + const wrapper = render( + null} + formattedDateComponent={() => null} + exceptionsUtilityComponent={() => null} + getFormattedComments={() => []} + /> + ); + // expect(wrapper).toMatchSnapshot(); + expect(wrapper.getByTestId('emptyViewerState')).toBeInTheDocument(); + expect(wrapper.queryByTestId('exceptionsContainer')).not.toBeInTheDocument(); + }); + + it('it renders no search results found prompt if "viewerStatus" is "empty_search"', () => { + const wrapper = render( + null} + formattedDateComponent={() => null} + exceptionsUtilityComponent={() => null} + getFormattedComments={() => []} + /> + ); + // expect(wrapper).toMatchSnapshot(); + expect(wrapper.getByTestId('emptySearchViewerState')).toBeInTheDocument(); + expect(wrapper.queryByTestId('exceptionsContainer')).not.toBeInTheDocument(); + }); + + it('it renders exceptions if "viewerStatus" and "null"', () => { + const wrapper = render( + null} + formattedDateComponent={() => null} + exceptionsUtilityComponent={() => null} + getFormattedComments={() => []} + /> + ); + // expect(wrapper).toMatchSnapshot(); + expect(wrapper.getByTestId('exceptionsContainer')).toBeTruthy(); + }); + }); + // TODO Add Exception Items and Pagination interactions +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.tsx new file mode 100644 index 000000000000..80ab3d99f6eb --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.tsx @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { css } from '@emotion/react'; +import type { FC } from 'react'; +import { EuiCommentProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import type { Pagination as PaginationType } from '@elastic/eui'; + +import type { + CommentsArray, + ExceptionListItemSchema, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; + +import { euiThemeVars } from '@kbn/ui-theme'; +import { EmptyViewerState, ExceptionItemCard, Pagination } from '../..'; + +import type { + RuleReferences, + ExceptionListItemIdentifiers, + ViewerStatus, + GetExceptionItemProps, +} from '../types'; + +const exceptionItemCss = css` + margin: ${euiThemeVars.euiSize} 0; + &div:first-child { + margin: ${euiThemeVars.euiSizeXS} 0 ${euiThemeVars.euiSize}; + } +`; + +interface ExceptionItemsProps { + lastUpdated: string | number | null; + viewerStatus: ViewerStatus; + isReadOnly: boolean; + emptyViewerTitle?: string; + emptyViewerBody?: string; + emptyViewerButtonText?: string; + exceptions: ExceptionListItemSchema[]; + listType: ExceptionListTypeEnum; + ruleReferences: RuleReferences; + pagination: PaginationType; + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + formattedDateComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + exceptionsUtilityComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + getFormattedComments: (comments: CommentsArray) => EuiCommentProps[]; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + onCreateExceptionListItem?: () => void; + onDeleteException: (arg: ExceptionListItemIdentifiers) => void; + onEditExceptionItem: (item: ExceptionListItemSchema) => void; + onPaginationChange: (arg: GetExceptionItemProps) => void; +} + +const ExceptionItemsComponent: FC = ({ + lastUpdated, + viewerStatus, + isReadOnly, + exceptions, + listType, + ruleReferences, + emptyViewerTitle, + emptyViewerBody, + emptyViewerButtonText, + pagination, + securityLinkAnchorComponent, + exceptionsUtilityComponent, + formattedDateComponent, + getFormattedComments, + onPaginationChange, + onDeleteException, + onEditExceptionItem, + onCreateExceptionListItem, +}) => { + const ExceptionsUtility = exceptionsUtilityComponent; + if (!exceptions.length || viewerStatus) + return ( + + ); + return ( + <> + + + + + {exceptions.map((exception) => ( + + + + ))} + + + + + + ); +}; + +ExceptionItemsComponent.displayName = 'ExceptionItemsComponent'; + +export const ExceptionItems = React.memo(ExceptionItemsComponent); + +ExceptionItems.displayName = 'ExceptionsItems'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx new file mode 100644 index 000000000000..4d97f198aa2b --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; + +import { Pagination } from './pagination'; + +describe('Pagination', () => { + it('it invokes "onPaginationChange" when per page item is clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = render( + + ); + + fireEvent.click(wrapper.getByTestId('tablePaginationPopoverButton')); + fireEvent.click(wrapper.getByTestId('tablePagination-50-rows')); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ + pagination: { pageIndex: 0, pageSize: 50, totalItemCount: 1 }, + }); + }); + + it('it invokes "onPaginationChange" when next clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = render( + + ); + + fireEvent.click(wrapper.getByTestId('pagination-button-next')); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ + pagination: { pageIndex: 1, pageSize: 5, totalItemCount: 160 }, + }); + }); + + it('it invokes "onPaginationChange" when page clicked', () => { + const mockOnPaginationChange = jest.fn(); + const wrapper = render( + + ); + + fireEvent.click(wrapper.getByTestId('pagination-button-2')); + + expect(mockOnPaginationChange).toHaveBeenCalledWith({ + pagination: { pageIndex: 2, pageSize: 50, totalItemCount: 160 }, + }); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx b/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx new file mode 100644 index 000000000000..30b029480e17 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { FC } from 'react'; +import { EuiTablePagination } from '@elastic/eui'; + +import type { PaginationProps } from '../types'; +import { usePagination } from './use_pagination'; + +const PaginationComponent: FC = ({ + dataTestSubj, + ariaLabel, + pagination, + onPaginationChange, +}) => { + const { + pageIndex, + pageCount, + pageSize, + pageSizeOptions, + + handleItemsPerPageChange, + handlePageIndexChange, + } = usePagination({ pagination, onPaginationChange }); + + return ( + + ); +}; + +PaginationComponent.displayName = 'PaginationComponent'; + +export const Pagination = React.memo(PaginationComponent); + +Pagination.displayName = 'Pagination'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts b/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts new file mode 100644 index 000000000000..d190c88f1061 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; +import { usePagination } from './use_pagination'; + +const onPaginationChange = jest.fn(); + +describe('usePagination', () => { + test('should return the correct EuiTablePagination props when all the pagination object properties are falsy', () => { + const pagination = { pageIndex: 0, pageSize: 0, totalItemCount: 0 }; + + const { result } = renderHook(() => usePagination({ pagination, onPaginationChange })); + const { pageCount, pageIndex, pageSize, pageSizeOptions } = result.current; + expect(pageCount).toEqual(0); + expect(pageIndex).toEqual(0); + expect(pageSize).toEqual(0); + expect(pageSizeOptions).toEqual(undefined); + }); + test('should return the correct pageCount when pagination properties are invalid', () => { + const pagination = { pageIndex: 0, pageSize: 10, totalItemCount: 0 }; + + const { result } = renderHook(() => usePagination({ pagination, onPaginationChange })); + const { pageCount } = result.current; + expect(pageCount).toEqual(0); + }); + test('should return the correct EuiTablePagination props when all the pagination object properties are turthy', () => { + const pagination = { pageIndex: 0, pageSize: 10, totalItemCount: 100 }; + + const { result } = renderHook(() => usePagination({ pagination, onPaginationChange })); + const { pageCount, pageIndex, pageSize } = result.current; + expect(pageCount).toEqual(10); + expect(pageIndex).toEqual(0); + expect(pageSize).toEqual(10); + }); + test('should call onPaginationChange with correct pageIndex when the Page changes', () => { + const pagination = { pageIndex: 0, pageSize: 10, totalItemCount: 100 }; + + const { result } = renderHook(() => usePagination({ pagination, onPaginationChange })); + const { handlePageIndexChange } = result.current; + + act(() => { + handlePageIndexChange(2); + }); + expect(onPaginationChange).toHaveBeenCalledWith({ + pagination: { pageIndex: 2, pageSize: 10, totalItemCount: 100 }, + }); + }); + test('should call onPaginationChange with correct pageSize when the number of items per change changes', () => { + const pagination = { pageIndex: 0, pageSize: 10, totalItemCount: 100 }; + + const { result } = renderHook(() => usePagination({ pagination, onPaginationChange })); + const { handleItemsPerPageChange } = result.current; + + act(() => { + handleItemsPerPageChange(100); + }); + expect(onPaginationChange).toHaveBeenCalledWith({ + pagination: { pageIndex: 0, pageSize: 100, totalItemCount: 100 }, + }); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts b/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts new file mode 100644 index 000000000000..d23565826368 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useCallback, useMemo } from 'react'; +import type { PaginationProps } from '../types'; + +export const usePagination = ({ pagination, onPaginationChange }: PaginationProps) => { + const { pageIndex, totalItemCount, pageSize, pageSizeOptions } = pagination; + + const pageCount = useMemo( + () => (isFinite(totalItemCount / pageSize) ? Math.ceil(totalItemCount / pageSize) : 0), + [pageSize, totalItemCount] + ); + + const handleItemsPerPageChange = useCallback( + (nextPageSize: number) => { + onPaginationChange({ + pagination: { + pageIndex, + pageSize: nextPageSize, + totalItemCount, + }, + }); + }, + [pageIndex, totalItemCount, onPaginationChange] + ); + + const handlePageIndexChange = useCallback( + (nextPageIndex: number) => { + onPaginationChange({ + pagination: { + pageIndex: nextPageIndex, + pageSize, + totalItemCount, + }, + }); + }, + [pageSize, totalItemCount, onPaginationChange] + ); + + return { + pageCount, + pageIndex, + pageSize, + pageSizeOptions, + + handleItemsPerPageChange, + handlePageIndexChange, + }; +}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx new file mode 100644 index 000000000000..ac82bb3b6e85 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { SearchBar } from './search_bar'; + +describe('SearchBar', () => { + it('it does not display add exception button if user is read only', () => { + const wrapper = render( + + ); + + expect(wrapper.queryByTestId('searchBarButton')).not.toBeInTheDocument(); + }); + + it('it invokes "onAddExceptionClick" when user selects to add an exception item', () => { + const mockOnAddExceptionClick = jest.fn(); + const wrapper = render( + + ); + + const searchBtn = wrapper.getByTestId('searchBarButton'); + + fireEvent.click(searchBtn); + expect(searchBtn).toHaveTextContent('Add rule exception'); + expect(mockOnAddExceptionClick).toHaveBeenCalledWith('detection'); + }); + + it('it invokes "onAddExceptionClick" when user selects to add an endpoint exception item', () => { + const mockOnAddExceptionClick = jest.fn(); + const wrapper = render( + + ); + + const searchBtn = wrapper.getByTestId('searchBarButton'); + + fireEvent.click(searchBtn); + expect(searchBtn).toHaveTextContent('Add endpoint exception'); + expect(mockOnAddExceptionClick).toHaveBeenCalledWith('endpoint'); + }); + it('it invokes the "handlOnSearch" when the user add search query', () => { + const mockHandleOnSearch = jest.fn(); + const wrapper = render( + + ); + + const searchInput = wrapper.getByTestId('searchBar'); + fireEvent.change(searchInput, { target: { value: 'query' } }); + expect(mockHandleOnSearch).toBeCalledWith({ search: 'query' }); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.tsx b/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.tsx new file mode 100644 index 000000000000..bb8dc6ee6255 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback } from 'react'; +import type { FC } from 'react'; + +import type { SearchFilterConfig } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSearchBar } from '@elastic/eui'; +import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { GetExceptionItemProps } from '../types'; + +const ITEMS_SCHEMA = { + strict: true, + fields: { + created_by: { + type: 'string', + }, + description: { + type: 'string', + }, + id: { + type: 'string', + }, + item_id: { + type: 'string', + }, + list_id: { + type: 'string', + }, + name: { + type: 'string', + }, + os_types: { + type: 'string', + }, + tags: { + type: 'string', + }, + }, +}; +interface SearchBarProps { + addExceptionButtonText?: string; + placeholdertext?: string; + canAddException?: boolean; // TODO what is the default value + + // TODO: REFACTOR: not to send the listType and handle it in the Parent + // Exception list type used to determine what type of item is + // being created when "onAddExceptionClick" is invoked + listType: ExceptionListTypeEnum; + isSearching?: boolean; + dataTestSubj?: string; + filters?: SearchFilterConfig[]; // TODO about filters + onSearch: (arg: GetExceptionItemProps) => void; + onAddExceptionClick: (type: ExceptionListTypeEnum) => void; +} +const SearchBarComponent: FC = ({ + addExceptionButtonText, + placeholdertext, + canAddException, + listType, + isSearching, + dataTestSubj, + filters = [], + onSearch, + onAddExceptionClick, +}) => { + const handleOnSearch = useCallback( + ({ queryText }): void => { + onSearch({ search: queryText }); + }, + [onSearch] + ); + + const handleAddException = useCallback(() => { + // TODO: ASK YARA why we need to send the listType + onAddExceptionClick(listType); + }, [onAddExceptionClick, listType]); + + return ( + + + + + {!canAddException && ( + + + {addExceptionButtonText} + + + )} + + ); +}; + +SearchBarComponent.displayName = 'SearchBarComponent'; + +export const SearchBar = React.memo(SearchBarComponent); + +SearchBar.displayName = 'SearchBar'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/translations.ts b/packages/kbn-securitysolution-exception-list-components/src/translations.ts new file mode 100644 index 000000000000..c919ef423c54 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/translations.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const EMPTY_VIEWER_STATE_EMPTY_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty.title', + { + defaultMessage: 'Add exceptions to this rule', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty.body', + { + defaultMessage: 'There is no exception in your rule. Create your first rule exception.', + } +); +export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty_search.search.title', + { + defaultMessage: 'No results match your search criteria', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty_search.body', + { + defaultMessage: 'Try modifying your search', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON = (exceptionType: string) => + i18n.translate('exceptionList-components.empty.viewer.state.empty.viewer_button', { + values: { exceptionType }, + defaultMessage: 'Create {exceptionType} exception', + }); + +export const EMPTY_VIEWER_STATE_ERROR_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.error_title', + { + defaultMessage: 'Unable to load exception items', + } +); + +export const EMPTY_VIEWER_STATE_ERROR_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.error_body', + { + defaultMessage: + 'There was an error loading the exception items. Contact your administrator for help.', + } +); diff --git a/packages/kbn-securitysolution-exception-list-components/src/types/index.ts b/packages/kbn-securitysolution-exception-list-components/src/types/index.ts new file mode 100644 index 000000000000..dbb402ca7845 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/types/index.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import type { Pagination } from '@elastic/eui'; +import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +export interface GetExceptionItemProps { + pagination?: Pagination; + search?: string; + filters?: string; +} + +export interface PaginationProps { + dataTestSubj?: string; + ariaLabel?: string; + pagination: Pagination; + onPaginationChange: (arg: GetExceptionItemProps) => void; +} + +export enum ViewerStatus { + ERROR = 'error', + EMPTY = 'empty', + EMPTY_SEARCH = 'empty_search', + LOADING = 'loading', + SEARCHING = 'searching', + DELETING = 'deleting', +} + +export interface ExceptionListSummaryProps { + pagination: Pagination; + // Corresponds to last time exception items were fetched + lastUpdated: string | number | null; +} + +export type ViewerFlyoutName = 'addException' | 'editException' | null; + +export interface RuleReferences { + [key: string]: any[]; // TODO fix +} + +export interface ExceptionListItemIdentifiers { + id: string; + name: string; + namespaceType: NamespaceType; +} + +export enum ListTypeText { + ENDPOINT = 'endpoint', + DETECTION = 'empty', + RULE_DEFAULT = 'empty_search', +} + +export interface RuleReference { + name: string; + id: string; + ruleId: string; + exceptionLists: ExceptionListSchema[]; +} diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts new file mode 100644 index 000000000000..472a80575f27 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { ValueWithSpaceWarning } from './value_with_space_warning'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts new file mode 100644 index 000000000000..8f6788d710a1 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useValueWithSpaceWarning } from './use_value_with_space_warning'; + +describe('useValueWithSpaceWarning', () => { + it('should return true when value is string and contains space', () => { + const { result } = renderHook(() => useValueWithSpaceWarning({ value: ' space before' })); + + const { showSpaceWarningIcon, warningText } = result.current; + expect(showSpaceWarningIcon).toBeTruthy(); + expect(warningText).toBeTruthy(); + }); + it('should return true when value is string and does not contain space', () => { + const { result } = renderHook(() => useValueWithSpaceWarning({ value: 'no space' })); + + const { showSpaceWarningIcon, warningText } = result.current; + expect(showSpaceWarningIcon).toBeFalsy(); + expect(warningText).toBeTruthy(); + }); + it('should return true when value is array and one of the elements contains space', () => { + const { result } = renderHook(() => + useValueWithSpaceWarning({ value: [' space before', 'no space'] }) + ); + + const { showSpaceWarningIcon, warningText } = result.current; + expect(showSpaceWarningIcon).toBeTruthy(); + expect(warningText).toBeTruthy(); + }); + it('should return true when value is array and none contains space', () => { + const { result } = renderHook(() => + useValueWithSpaceWarning({ value: ['no space', 'no space'] }) + ); + + const { showSpaceWarningIcon, warningText } = result.current; + expect(showSpaceWarningIcon).toBeFalsy(); + expect(warningText).toBeTruthy(); + }); + it('should return the tooltipIconText', () => { + const { result } = renderHook(() => + useValueWithSpaceWarning({ value: ' space before', tooltipIconText: 'Warning Text' }) + ); + + const { showSpaceWarningIcon, warningText } = result.current; + expect(showSpaceWarningIcon).toBeTruthy(); + expect(warningText).toEqual('Warning Text'); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts new file mode 100644 index 000000000000..bf407d2798c7 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { paramContainsSpace, autoCompletei18n } from '@kbn/securitysolution-autocomplete'; + +interface UseValueWithSpaceWarningResult { + showSpaceWarningIcon: boolean; + warningText: string; +} +interface UseValueWithSpaceWarningProps { + value: string | string[]; + tooltipIconText?: string; +} + +export const useValueWithSpaceWarning = ({ + value, + tooltipIconText, +}: UseValueWithSpaceWarningProps): UseValueWithSpaceWarningResult => { + const showSpaceWarningIcon = Array.isArray(value) + ? value.find(paramContainsSpace) + : paramContainsSpace(value); + + return { + showSpaceWarningIcon: !!showSpaceWarningIcon, + warningText: tooltipIconText || autoCompletei18n.FIELD_SPACE_WARNING, + }; +}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx new file mode 100644 index 000000000000..e19a54be48aa --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; + +import { ValueWithSpaceWarning } from '.'; + +import * as useValueWithSpaceWarningMock from './use_value_with_space_warning'; + +jest.mock('./use_value_with_space_warning'); + +describe('ValueWithSpaceWarning', () => { + beforeEach(() => { + // @ts-ignore + useValueWithSpaceWarningMock.useValueWithSpaceWarning = jest + .fn() + .mockReturnValue({ showSpaceWarningIcon: true, warningText: 'Warning Text' }); + }); + it('should not render if value is falsy', () => { + const container = render(); + expect(container.queryByTestId('valueWithSpaceWarningTooltip')).toBeFalsy(); + }); + it('should not render if showSpaceWarning is falsy', () => { + // @ts-ignore + useValueWithSpaceWarningMock.useValueWithSpaceWarning = jest + .fn() + .mockReturnValue({ showSpaceWarningIcon: false, warningText: '' }); + + const container = render(); + expect(container.queryByTestId('valueWithSpaceWarningTooltip')).toBeFalsy(); + }); + it('should render if showSpaceWarning is truthy', () => { + const container = render(); + expect(container.getByTestId('valueWithSpaceWarningTooltip')).toBeInTheDocument(); + }); + it('should show the tooltip when the icon is clicked', async () => { + const container = render(); + + fireEvent.mouseOver(container.getByTestId('valueWithSpaceWarningTooltip')); + expect(await container.findByText('Warning Text')).toBeInTheDocument(); + }); +}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx new file mode 100644 index 000000000000..9cff0649efb9 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { FC } from 'react'; +import { css } from '@emotion/css'; +import { euiThemeVars } from '@kbn/ui-theme'; + +import { EuiIcon, EuiToolTip } from '@elastic/eui'; +import { useValueWithSpaceWarning } from './use_value_with_space_warning'; + +interface ValueWithSpaceWarningProps { + value: string[] | string; + tooltipIconType?: string; + tooltipIconText?: string; +} +const containerCss = css` + display: inline; + margin-left: ${euiThemeVars.euiSizeXS}; +`; +export const ValueWithSpaceWarning: FC = ({ + value, + tooltipIconType = 'iInCircle', + tooltipIconText, +}) => { + const { showSpaceWarningIcon, warningText } = useValueWithSpaceWarning({ + value, + tooltipIconText, + }); + if (!showSpaceWarningIcon || !value) return null; + return ( +
    + + + +
    + ); +}; diff --git a/packages/kbn-securitysolution-exception-list-components/tsconfig.json b/packages/kbn-securitysolution-exception-list-components/tsconfig.json new file mode 100644 index 000000000000..412652e0a8f9 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.d.ts" + ] +} diff --git a/x-pack/plugins/security_solution/public/jest.config.js b/x-pack/plugins/security_solution/public/jest.config.js index 5eb349b2c16e..afa3e1b47efd 100644 --- a/x-pack/plugins/security_solution/public/jest.config.js +++ b/x-pack/plugins/security_solution/public/jest.config.js @@ -14,7 +14,17 @@ module.exports = { coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/security_solution/public', coverageReporters: ['text', 'html'], - collectCoverageFrom: ['/x-pack/plugins/security_solution/public/**/*.{ts,tsx}'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution/public/**/*.{ts,tsx}', + '!/x-pack/plugins/security_solution/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution/public/{__test__,__snapshots__,__examples__,*mock*,tests,test_helpers,integration_tests,types}/**/*', + '!/x-pack/plugins/security_solution/public/*mock*.{ts,tsx}', + '!/x-pack/plugins/security_solution/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution/public/*.d.ts', + '!/x-pack/plugins/security_solution/public/*.config.ts', + '!/x-pack/plugins/security_solution/public/index.{js,ts,tsx}', + ], + // See: https://github.com/elastic/kibana/issues/117255, the moduleNameMapper creates mocks to avoid memory leaks from kibana core. moduleNameMapper: { 'core/server$': '/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts', diff --git a/yarn.lock b/yarn.lock index 085f55146d4e..4996c786772d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3566,6 +3566,10 @@ version "0.0.0" uid "" +"@kbn/securitysolution-exception-list-components@link:bazel-bin/packages/kbn-securitysolution-exception-list-components": + version "0.0.0" + uid "" + "@kbn/securitysolution-hook-utils@link:bazel-bin/packages/kbn-securitysolution-hook-utils": version "0.0.0" uid "" @@ -7683,6 +7687,10 @@ version "0.0.0" uid "" +"@types/kbn__securitysolution-exception-list-components@link:bazel-bin/packages/kbn-securitysolution-exception-list-components/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__securitysolution-hook-utils@link:bazel-bin/packages/kbn-securitysolution-hook-utils/npm_module_types": version "0.0.0" uid "" From 8c855d9fc286489921a058ce0999a5673ba47e04 Mon Sep 17 00:00:00 2001 From: Rickyanto Ang Date: Wed, 28 Sep 2022 11:44:26 -0700 Subject: [PATCH 077/185] [8.5][Session View][Test] Added Unit test for Beta tag removal+ Price tier changes (#141874) * added test case for timeline-body-actions * added test for action button count * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../events_tab/events_query_tab_body.test.tsx | 44 +++++++++++ .../components/sessions_viewer/index.test.tsx | 57 ++++++++++++++ .../timeline/body/actions/index.test.tsx | 76 ++++++++++++++++++- 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx index 2058cd4a7c61..0b13363a653a 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.test.tsx @@ -14,6 +14,12 @@ import type { EventsQueryTabBodyComponentProps } from './events_query_tab_body'; import { EventsQueryTabBody, ALERTS_EVENTS_HISTOGRAM_ID } from './events_query_tab_body'; import { useGlobalFullScreen } from '../../containers/use_full_screen'; import * as tGridActions from '@kbn/timelines-plugin/public/store/t_grid/actions'; +import { licenseService } from '../../hooks/use_license'; + +const mockGetDefaultControlColumn = jest.fn(); +jest.mock('../../../timelines/components/timeline/body/control_columns', () => ({ + getDefaultControlColumn: (props: number) => mockGetDefaultControlColumn(props), +})); jest.mock('../../lib/kibana', () => { const original = jest.requireActual('../../lib/kibana'); @@ -47,6 +53,19 @@ jest.mock('../../containers/use_full_screen', () => ({ }), })); +jest.mock('../../hooks/use_license', () => { + const licenseServiceInstance = { + isPlatinumPlus: jest.fn(), + isEnterprise: jest.fn(() => false), + }; + return { + licenseService: licenseServiceInstance, + useLicense: () => { + return licenseServiceInstance; + }, + }; +}); + describe('EventsQueryTabBody', () => { const commonProps: EventsQueryTabBodyComponentProps = { indexNames: ['test-index'], @@ -69,6 +88,7 @@ describe('EventsQueryTabBody', () => { ); expect(queryByText('MockedStatefulEventsViewer')).toBeInTheDocument(); + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(4); }); it('renders the matrix histogram when globalFullScreen is false', () => { @@ -147,4 +167,28 @@ describe('EventsQueryTabBody', () => { expect(spy).toHaveBeenCalled(); }); + + it('only have 4 columns on Action bar for non-Enterprise user', () => { + render( + + + + ); + + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(4); + }); + + it('shows 5 columns on Action bar for Enterprise user', () => { + const licenseServiceMock = licenseService as jest.Mocked; + + licenseServiceMock.isEnterprise.mockReturnValue(true); + + render( + + + + ); + + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(5); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx index a89a61897232..47a1bc4fa651 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx @@ -14,6 +14,7 @@ import { TimelineId } from '@kbn/timelines-plugin/common'; import type { SessionsComponentsProps } from './types'; import type { TimelineModel } from '../../../timelines/store/timeline/model'; import { useGetUserCasesPermissions } from '../../lib/kibana'; +import { licenseService } from '../../hooks/use_license'; jest.mock('../../lib/kibana'); @@ -47,10 +48,28 @@ type Props = Partial & { entityType: EntityType; }; +const mockGetDefaultControlColumn = jest.fn(); +jest.mock('../../../timelines/components/timeline/body/control_columns', () => ({ + getDefaultControlColumn: (props: number) => mockGetDefaultControlColumn(props), +})); + const TEST_PREFIX = 'security_solution:sessions_viewer:sessions_view'; const callFilters = jest.fn(); +jest.mock('../../hooks/use_license', () => { + const licenseServiceInstance = { + isPlatinumPlus: jest.fn(), + isEnterprise: jest.fn(() => false), + }; + return { + licenseService: licenseServiceInstance, + useLicense: () => { + return licenseServiceInstance; + }, + }; +}); + // creating a dummy component for testing TGrid to avoid mocking all the implementation details // but still test if the TGrid will render properly const SessionsViewerTGrid: React.FC = ({ columns, start, end, id, filters, entityType }) => { @@ -144,4 +163,42 @@ describe('SessionsView', () => { ]); }); }); + it('Action tab should have 4 columns for non Enterprise users', async () => { + render( + + + + ); + + await waitFor(() => { + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(4); + }); + }); + + it('Action tab should have 5 columns for Enterprise or above users', async () => { + const licenseServiceMock = licenseService as jest.Mocked; + + licenseServiceMock.isEnterprise.mockReturnValue(true); + render( + + + + ); + + await waitFor(() => { + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(5); + }); + }); + + it('Action tab should have 5 columns when accessed via K8S dahsboard', async () => { + render( + + + + ); + + await waitFor(() => { + expect(mockGetDefaultControlColumn).toHaveBeenCalledWith(5); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx index 004850ede7d8..7bc64b990064 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx @@ -7,12 +7,13 @@ import { mount } from 'enzyme'; import React from 'react'; - +import { TimelineId } from '../../../../../../common/types/timeline'; import { TestProviders, mockTimelineModel, mockTimelineData } from '../../../../../common/mock'; import { Actions, isAlert } from '.'; import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; +import { licenseService } from '../../../../../common/hooks/use_license'; jest.mock('../../../../../detections/components/user_info', () => ({ useUserData: jest.fn().mockReturnValue([{ canUserCRUD: true, hasIndexWrite: true }]), @@ -63,6 +64,19 @@ jest.mock('../../../../../common/lib/kibana', () => { }; }); +jest.mock('../../../../../common/hooks/use_license', () => { + const licenseServiceInstance = { + isPlatinumPlus: jest.fn(), + isEnterprise: jest.fn(() => false), + }; + return { + licenseService: licenseServiceInstance, + useLicense: () => { + return licenseServiceInstance; + }, + }; +}); + const defaultProps = { ariaRowindex: 2, checked: false, @@ -122,6 +136,7 @@ describe('Actions', () => { expect(wrapper.find('[data-test-subj="select-event"]').exists()).toBe(false); }); + describe('Alert context menu enabled?', () => { test('it disables for eventType=raw', () => { const wrapper = mount( @@ -225,6 +240,65 @@ describe('Actions', () => { expect(wrapper.find('[data-test-subj="view-in-analyzer"]').exists()).toBe(false); }); + + test('it should not show session view button on action tabs for basic users', () => { + const ecsData = { + ...mockTimelineData[0].ecs, + event: { kind: ['alert'] }, + agent: { type: ['endpoint'] }, + process: { entry_leader: { entity_id: ['test_id'] } }, + }; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(false); + }); + + test('it should show session view button on action tabs when user access the session viewer via K8S dashboard', () => { + const ecsData = { + ...mockTimelineData[0].ecs, + event: { kind: ['alert'] }, + agent: { type: ['endpoint'] }, + process: { entry_leader: { entity_id: ['test_id'] } }, + }; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(true); + }); + + test('it should show session view button on action tabs for enterprise users', () => { + const licenseServiceMock = licenseService as jest.Mocked; + + licenseServiceMock.isEnterprise.mockReturnValue(true); + + const ecsData = { + ...mockTimelineData[0].ecs, + event: { kind: ['alert'] }, + agent: { type: ['endpoint'] }, + process: { entry_leader: { entity_id: ['test_id'] } }, + }; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(true); + }); }); describe('isAlert', () => { From aff59ee0161c5ade3033afdf69a5ac57c5ed4e4b Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Wed, 28 Sep 2022 20:50:55 +0200 Subject: [PATCH 078/185] Add infrastructure category (#142115) --- src/plugins/custom_integrations/common/index.ts | 1 + x-pack/plugins/fleet/common/types/models/package_spec.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/plugins/custom_integrations/common/index.ts b/src/plugins/custom_integrations/common/index.ts index a3ab30a526ca..e52a5deac59e 100755 --- a/src/plugins/custom_integrations/common/index.ts +++ b/src/plugins/custom_integrations/common/index.ts @@ -23,6 +23,7 @@ export const INTEGRATION_CATEGORY_DISPLAY = { datastore: 'Datastore', elastic_stack: 'Elastic Stack', google_cloud: 'Google Cloud', + infrastructure: 'Infrastructure', kubernetes: 'Kubernetes', languages: 'Languages', message_queue: 'Message queue', diff --git a/x-pack/plugins/fleet/common/types/models/package_spec.ts b/x-pack/plugins/fleet/common/types/models/package_spec.ts index 4463bb81097e..52f993499ea4 100644 --- a/x-pack/plugins/fleet/common/types/models/package_spec.ts +++ b/x-pack/plugins/fleet/common/types/models/package_spec.ts @@ -42,6 +42,7 @@ export type PackageSpecCategory = | 'datastore' | 'elastic_stack' | 'google_cloud' + | 'infrastructure' | 'kubernetes' | 'languages' | 'message_queue' From 1717d61a76134b50df31dba437c4142310b70ab9 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:51:14 +0200 Subject: [PATCH 079/185] [Fleet] moved Agent activity and Add agent buttons to a new row above search bar (#142046) * moved Agent activity and Add agent buttons to a new row above search bar * moved show bulk action button visibility logic one level up --- .../components/bulk_actions.tsx | 59 ++++----- ...est.tsx => search_and_filter_bar.test.tsx} | 53 ++++++-- .../components/search_and_filter_bar.tsx | 121 +++++++++--------- 3 files changed, 132 insertions(+), 101 deletions(-) rename x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/{bulk_actions.test.tsx => search_and_filter_bar.test.tsx} (67%) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx index 10ced1a5c032..356753a0d004 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx @@ -6,7 +6,6 @@ */ import React, { useMemo, useState } from 'react'; -import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, @@ -32,9 +31,6 @@ import { getCommonTags } from '../utils'; import type { SelectionMode } from './types'; import { TagsAddRemove } from './tags_add_remove'; -const FlexItem = styled(EuiFlexItem)` - height: ${(props) => props.theme.eui.euiSizeL}; -`; export interface Props { totalAgents: number; totalInactiveAgents: number; @@ -233,38 +229,31 @@ export const AgentBulkActions: React.FunctionComponent = ({ /> )} - {(selectionMode === 'manual' && selectedAgents.length) || - (selectionMode === 'query' && totalAgents > 0) ? ( - <> - - - - - } - isOpen={isMenuOpen} - closePopover={closeMenu} - panelPaddingSize="none" - anchorPosition="downLeft" + + - - - - - ) : ( - - )} + + + } + isOpen={isMenuOpen} + closePopover={closeMenu} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx similarity index 67% rename from x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx rename to x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx index 71e673fd30e1..33fd16419a1b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx @@ -20,8 +20,7 @@ import { FleetStatusProvider, ConfigContext, KibanaVersionContext } from '../../ import { getMockTheme } from '../../../../../../mocks'; -import { AgentBulkActions } from './bulk_actions'; -import type { Props } from './bulk_actions'; +import { SearchAndFilterBar } from './search_and_filter_bar'; const mockTheme = getMockTheme({ eui: { @@ -29,13 +28,19 @@ const mockTheme = getMockTheme({ }, }); -const TestComponent = (props: Props) => ( +jest.mock('../../../../components', () => { + return { + SearchBar: () =>
    , + }; +}); + +const TestComponent = (props: any) => ( - + @@ -43,10 +48,10 @@ const TestComponent = (props: Props) => ( ); -describe('AgentBulkActions', () => { +describe('SearchAndFilterBar', () => { it('should show no Actions button when no agent is selected', async () => { const selectedAgents: Agent[] = []; - const props: Props = { + const props: any = { totalAgents: 10, totalInactiveAgents: 2, selectionMode: 'manual', @@ -54,8 +59,12 @@ describe('AgentBulkActions', () => { selectedAgents, refreshAgents: () => undefined, visibleAgents: [], - allTags: [], + tags: [], agentPolicies: [], + selectedStatus: [], + selectedTags: [], + selectedAgentPolicies: [], + showAgentActivityTour: {}, }; const testBed = registerTestBed(TestComponent)(props); const { exists } = testBed; @@ -76,7 +85,7 @@ describe('AgentBulkActions', () => { local_metadata: {}, }, ]; - const props: Props = { + const props: any = { totalAgents: 10, totalInactiveAgents: 2, selectionMode: 'manual', @@ -84,8 +93,34 @@ describe('AgentBulkActions', () => { selectedAgents, refreshAgents: () => undefined, visibleAgents: [], - allTags: [], + tags: [], + agentPolicies: [], + selectedStatus: [], + selectedTags: [], + selectedAgentPolicies: [], + showAgentActivityTour: {}, + }; + const testBed = registerTestBed(TestComponent)(props); + const { exists } = testBed; + + expect(exists('agentBulkActionsButton')).not.toBeNull(); + }); + + it('should show an Actions button when agents selected in query mode', async () => { + const props: any = { + totalAgents: 10, + totalInactiveAgents: 2, + selectionMode: 'query', + currentQuery: '', + selectedAgents: [], + refreshAgents: () => undefined, + visibleAgents: [], + tags: [], agentPolicies: [], + selectedStatus: [], + selectedTags: [], + selectedAgentPolicies: [], + showAgentActivityTour: {}, }; const testBed = registerTestBed(TestComponent)(props); const { exists } = testBed; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx index e90e1ae71322..9b16136df2d9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx @@ -69,6 +69,10 @@ const ClearAllTagsFilterItem = styled(EuiFilterSelectItem)` padding: ${(props) => props.theme.eui.euiSizeS}; `; +const FlexEndEuiFlexItem = styled(EuiFlexItem)` + align-self: flex-end; +`; + export const SearchAndFilterBar: React.FunctionComponent<{ agentPolicies: AgentPolicy[]; draftKuery: string; @@ -151,7 +155,51 @@ export const SearchAndFilterBar: React.FunctionComponent<{ return ( <> {/* Search and filter bar */} - + + + + + + + + + } + > + + + + + + + + } + > + + + + + + + @@ -328,63 +376,22 @@ export const SearchAndFilterBar: React.FunctionComponent<{ - {selectedAgents.length === 0 && ( - - - } - > - - - - + {(selectionMode === 'manual' && selectedAgents.length) || + (selectionMode === 'query' && totalAgents > 0) ? ( + + - )} - - - - {selectedAgents.length === 0 && ( - - - } - > - - - - - - )} - - - + ) : null} From 2d44c3597864288390951704f9beddd7212c199e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 20:09:50 +0100 Subject: [PATCH 080/185] skip flaky suite (#107034) --- test/plugin_functional/test_suites/telemetry/telemetry.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/plugin_functional/test_suites/telemetry/telemetry.ts b/test/plugin_functional/test_suites/telemetry/telemetry.ts index 3b087c2705c1..1c68abd5426d 100644 --- a/test/plugin_functional/test_suites/telemetry/telemetry.ts +++ b/test/plugin_functional/test_suites/telemetry/telemetry.ts @@ -14,7 +14,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const browser = getService('browser'); const PageObjects = getPageObjects(['common']); - describe('Telemetry service', () => { + // FLAKY: https://github.com/elastic/kibana/issues/107034 + describe.skip('Telemetry service', () => { const checkCanSendTelemetry = (): Promise => { return browser.executeAsync((cb) => { (window as unknown as Record Promise>) From 0e6d079bfe058fa6e02eb29ec195957f1896e595 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 28 Sep 2022 20:12:29 +0100 Subject: [PATCH 081/185] skip flaky suite (#140546) --- .../test/security_solution_endpoint/apps/endpoint/responder.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts index 9caf23108986..ddcbbc6251fd 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts @@ -116,7 +116,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - describe('from timeline', () => { + // FLAKY: https://github.com/elastic/kibana/issues/140546 + describe.skip('from timeline', () => { let timeline: TimelineResponse; before(async () => { From f5b6a2c43ba88232b9789b96d562b230a3ebc362 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 28 Sep 2022 15:16:51 -0400 Subject: [PATCH 082/185] [Portable Dashboards] Remove Saved Object Class (#138774) * Removed dashboard saved object class, replaced with dashboard saved object service --- .../public/services/plugin_services.stub.ts | 11 +- .../public/services/plugin_services.ts | 11 +- .../embeddable/embeddable_references.test.ts | 76 ----- .../embeddable/embeddable_references.ts | 69 ---- .../dashboard/common/embeddable/types.ts | 16 - src/plugins/dashboard/common/index.ts | 32 +- .../dashboard_panel_converters.test.ts} | 2 +- .../dashboard_panel_converters.ts} | 16 +- .../dashboard_container_references.test.ts} | 2 +- .../dashboard_container_references.ts} | 0 ...dashboard_saved_object_references.test.ts} | 4 +- .../dashboard_saved_object_references.ts} | 108 +++--- src/plugins/dashboard/common/types.ts | 138 ++++---- .../actions/add_to_library_action.test.tsx | 2 +- .../actions/add_to_library_action.tsx | 3 +- .../actions/clone_panel_action.test.tsx | 2 +- .../actions/clone_panel_action.tsx | 5 +- .../actions/copy_to_dashboard_action.tsx | 3 +- .../actions/copy_to_dashboard_modal.tsx | 4 +- .../actions/expand_panel_action.tsx | 4 +- .../filters_notification_badge.test.tsx | 21 +- .../public/application/actions/index.ts | 93 +++-- .../library_notification_action.test.tsx | 11 +- .../actions/library_notification_action.tsx | 6 +- .../actions/library_notification_popover.tsx | 4 +- .../actions/replace_panel_action.tsx | 3 +- .../unlink_from_library_action.test.tsx | 4 +- .../actions/unlink_from_library_action.tsx | 3 +- .../public/application/dashboard_app.tsx | 30 +- .../public/application/dashboard_router.tsx | 111 +++--- .../embeddable/dashboard_constants.ts | 13 - .../embeddable/dashboard_container.tsx | 2 +- .../dashboard_container_factory.tsx | 16 +- .../embeddable/grid/dashboard_grid.tsx | 6 +- .../public/application/embeddable/index.ts | 7 - .../panel/create_panel_state.test.ts | 2 +- .../embeddable/panel/create_panel_state.ts | 2 +- .../panel/dashboard_panel_placement.ts | 4 +- .../embeddable/placeholder/index.ts | 5 +- .../placeholder/placeholder_embeddable.tsx | 4 +- .../placeholder_embeddable_factory.ts | 3 +- .../hooks/use_dashboard_app_state.test.tsx | 126 +++---- .../hooks/use_dashboard_app_state.ts | 169 ++++------ .../lib/build_dashboard_container.ts | 8 +- .../lib/convert_dashboard_panels.ts | 32 -- .../lib/convert_dashboard_state.ts | 69 +--- .../lib/dashboard_control_group.ts | 32 +- ... => dashboard_session_restoration.test.ts} | 9 +- .../lib/dashboard_session_restoration.ts | 15 +- .../application/lib/dashboard_tagging.ts | 31 -- .../application/lib/diff_dashboard_state.ts | 71 +++- .../public/application/lib/filter_utils.ts | 23 +- .../dashboard/public/application/lib/index.ts | 32 +- .../lib/load_dashboard_by_title.ts | 31 -- .../load_dashboard_history_location_state.ts | 4 +- .../lib/load_saved_dashboard_state.ts | 74 ---- .../application/lib/migrate_app_state.test.ts | 164 --------- .../application/lib/migrate_app_state.ts | 87 ----- .../public/application/lib/save_dashboard.ts | 120 ------- .../lib/sync_dashboard_container_input.ts | 14 +- .../lib/sync_dashboard_filter_state.ts | 52 +-- .../lib/sync_dashboard_url_state.ts | 46 ++- .../public/application/lib/url.test.ts | 35 -- .../dashboard/public/application/lib/url.ts | 24 -- .../listing/dashboard_listing.test.tsx | 121 +++---- .../application/listing/dashboard_listing.tsx | 81 +++-- .../dashboard_unsaved_listing.test.tsx | 89 ++--- .../listing/dashboard_unsaved_listing.tsx | 49 ++- .../state/dashboard_state_slice.ts | 13 +- .../test_helpers/get_saved_dashboard_mock.ts | 36 -- .../public/application/test_helpers/index.ts | 2 - .../test_helpers/make_default_services.ts | 48 --- .../application/top_nav/dashboard_top_nav.tsx | 240 +++++++------ .../top_nav/show_share_modal.test.tsx | 8 +- .../application/top_nav/show_share_modal.tsx | 31 +- .../dashboard/public/dashboard_constants.ts | 35 ++ .../dashboard/public/dashboard_strings.ts | 16 +- src/plugins/dashboard/public/index.ts | 7 +- src/plugins/dashboard/public/locator.ts | 2 +- src/plugins/dashboard/public/plugin.tsx | 129 ++----- .../saved_dashboards/saved_dashboard.ts | 200 ----------- .../saved_dashboards/saved_dashboards.ts | 36 -- .../dashboard_saved_object.stub.ts | 77 +++++ .../dashboard_saved_object_service.ts | 62 ++++ .../check_for_duplicate_dashboard_title.ts | 62 ++++ .../lib/find_dashboard_saved_objects.ts | 93 +++++ .../load_dashboard_state_from_saved_object.ts | 201 +++++++++++ .../save_dashboard_state_to_saved_object.ts | 182 ++++++++++ .../services/dashboard_saved_object/types.ts | 63 ++++ .../services/embeddable/embeddable.stub.ts | 6 +- .../services/embeddable/embeddable_service.ts | 23 +- .../public/services/embeddable/types.ts | 17 +- .../public/services/plugin_services.stub.ts | 4 +- .../public/services/plugin_services.ts | 22 +- .../public/services/saved_object_loader.ts | 180 ---------- .../saved_objects/saved_objects.stub.ts | 21 -- .../saved_objects/saved_objects_service.ts | 26 -- .../public/services/saved_objects/types.ts | 13 - .../saved_objects_tagging.stub.ts | 5 +- .../saved_objects_tagging_service.ts | 12 +- .../services/saved_objects_tagging/types.ts | 5 +- .../dashboard/public/services/types.ts | 7 +- src/plugins/dashboard/public/types.ts | 30 +- .../dashboard_container_embeddable_factory.ts | 5 +- .../saved_objects/dashboard_migrations.ts | 318 ------------------ ...dashboard.ts => dashboard_saved_object.ts} | 2 +- .../dashboard/server/saved_objects/index.ts | 2 +- .../server/saved_objects/is_dashboard_doc.ts | 37 -- ...dashboard_saved_object_migrations.test.ts} | 14 +- .../dashboard_saved_object_migrations.ts | 48 +++ .../migrate_by_value_dashboard_panels.ts | 97 ++++++ .../migrate_extract_panel_references.ts | 56 +++ .../migrations/migrate_hidden_titles.ts | 65 ++++ .../migrate_index_pattern_reference.test.ts} | 2 +- .../migrate_index_pattern_reference.ts} | 0 .../migrate_match_all_query.test.ts | 0 .../migrate_match_all_query.ts | 2 +- .../migrations/migrate_to_730}/index.ts | 5 +- .../migrate_to_730_panels.test.ts | 7 +- .../migrate_to_730}/migrate_to_730_panels.ts | 21 +- .../migrate_to_730/migrations_700.ts | 90 +++++ .../migrate_to_730}/migrations_730.test.ts | 13 +- .../migrate_to_730}/migrations_730.ts | 35 +- .../move_filters_to_query.test.ts | 0 .../migrate_to_730}/move_filters_to_query.ts | 0 .../migrations/migrate_to_730/readme.md | 3 + .../migrations/migrate_to_730/types.ts | 182 ++++++++++ .../server/usage/dashboard_telemetry.test.ts | 12 +- .../server/usage/dashboard_telemetry.ts | 4 +- .../dashboard_telemetry_collection_task.ts | 13 +- .../usage/find_by_value_embeddables.test.ts | 8 +- .../server/usage/find_by_value_embeddables.ts | 4 +- .../apps/dashboard/group3/bwc_shared_urls.ts | 33 +- .../apps/dashboard/group3/dashboard_state.ts | 2 +- .../apps/dashboard/group4/dashboard_empty.ts | 2 +- .../functional/page_objects/dashboard_page.ts | 26 ++ .../add_swimlane_to_dashboard_controls.tsx | 4 +- .../use_dashboards_table.tsx | 4 +- .../application/services/dashboard_service.ts | 5 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../feature_controls/dashboard_security.ts | 155 +++------ .../feature_controls/dashboard_spaces.ts | 67 ++-- 144 files changed, 2575 insertions(+), 3151 deletions(-) delete mode 100644 src/plugins/dashboard/common/embeddable/embeddable_references.test.ts delete mode 100644 src/plugins/dashboard/common/embeddable/embeddable_references.ts delete mode 100644 src/plugins/dashboard/common/embeddable/types.ts rename src/plugins/dashboard/common/{embeddable/embeddable_saved_object_converters.test.ts => lib/dashboard_panel_converters.test.ts} (98%) rename src/plugins/dashboard/common/{embeddable/embeddable_saved_object_converters.ts => lib/dashboard_panel_converters.ts} (75%) rename src/plugins/dashboard/common/{embeddable/dashboard_container_persistable_state.test.ts => persistable_state/dashboard_container_references.test.ts} (99%) rename src/plugins/dashboard/common/{embeddable/dashboard_container_persistable_state.ts => persistable_state/dashboard_container_references.ts} (100%) rename src/plugins/dashboard/common/{saved_dashboard_references.test.ts => persistable_state/dashboard_saved_object_references.test.ts} (98%) rename src/plugins/dashboard/common/{saved_dashboard_references.ts => persistable_state/dashboard_saved_object_references.ts} (96%) delete mode 100644 src/plugins/dashboard/public/application/embeddable/dashboard_constants.ts delete mode 100644 src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts rename src/plugins/dashboard/public/application/lib/{session_restoration.test.ts => dashboard_session_restoration.test.ts} (89%) delete mode 100644 src/plugins/dashboard/public/application/lib/dashboard_tagging.ts delete mode 100644 src/plugins/dashboard/public/application/lib/load_dashboard_by_title.ts delete mode 100644 src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts delete mode 100644 src/plugins/dashboard/public/application/lib/migrate_app_state.test.ts delete mode 100644 src/plugins/dashboard/public/application/lib/migrate_app_state.ts delete mode 100644 src/plugins/dashboard/public/application/lib/save_dashboard.ts delete mode 100644 src/plugins/dashboard/public/application/lib/url.test.ts delete mode 100644 src/plugins/dashboard/public/application/lib/url.ts delete mode 100644 src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts delete mode 100644 src/plugins/dashboard/public/application/test_helpers/make_default_services.ts delete mode 100644 src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts delete mode 100644 src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object_service.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts create mode 100644 src/plugins/dashboard/public/services/dashboard_saved_object/types.ts delete mode 100644 src/plugins/dashboard/public/services/saved_object_loader.ts delete mode 100644 src/plugins/dashboard/public/services/saved_objects/saved_objects.stub.ts delete mode 100644 src/plugins/dashboard/public/services/saved_objects/saved_objects_service.ts delete mode 100644 src/plugins/dashboard/public/services/saved_objects/types.ts delete mode 100644 src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts rename src/plugins/dashboard/server/saved_objects/{dashboard.ts => dashboard_saved_object.ts} (97%) delete mode 100644 src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts rename src/plugins/dashboard/server/saved_objects/{dashboard_migrations.test.ts => migrations/dashboard_saved_object_migrations.test.ts} (99%) create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.ts create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_by_value_dashboard_panels.ts create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_hidden_titles.ts rename src/plugins/dashboard/server/saved_objects/{replace_index_pattern_reference.test.ts => migrations/migrate_index_pattern_reference.test.ts} (94%) rename src/plugins/dashboard/server/saved_objects/{replace_index_pattern_reference.ts => migrations/migrate_index_pattern_reference.ts} (100%) rename src/plugins/dashboard/server/saved_objects/{ => migrations}/migrate_match_all_query.test.ts (100%) rename src/plugins/dashboard/server/saved_objects/{ => migrations}/migrate_match_all_query.ts (100%) rename src/plugins/dashboard/{public/saved_dashboards => server/saved_objects/migrations/migrate_to_730}/index.ts (73%) rename src/plugins/dashboard/{common => server/saved_objects/migrations/migrate_to_730}/migrate_to_730_panels.test.ts (99%) rename src/plugins/dashboard/{common => server/saved_objects/migrations/migrate_to_730}/migrate_to_730_panels.ts (98%) create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_700.ts rename src/plugins/dashboard/server/saved_objects/{ => migrations/migrate_to_730}/migrations_730.test.ts (96%) rename src/plugins/dashboard/server/saved_objects/{ => migrations/migrate_to_730}/migrations_730.ts (69%) rename src/plugins/dashboard/server/saved_objects/{ => migrations/migrate_to_730}/move_filters_to_query.test.ts (100%) rename src/plugins/dashboard/server/saved_objects/{ => migrations/migrate_to_730}/move_filters_to_query.ts (100%) create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/readme.md create mode 100644 src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/types.ts diff --git a/src/plugins/controls/public/services/plugin_services.stub.ts b/src/plugins/controls/public/services/plugin_services.stub.ts index 485891ff6ef9..08be260ce052 100644 --- a/src/plugins/controls/public/services/plugin_services.stub.ts +++ b/src/plugins/controls/public/services/plugin_services.stub.ts @@ -27,16 +27,15 @@ import { themeServiceFactory } from './theme/theme.story'; import { registry as stubRegistry } from './plugin_services.story'; export const providers: PluginServiceProviders = { - http: new PluginServiceProvider(httpServiceFactory), + controls: new PluginServiceProvider(controlsServiceFactory), data: new PluginServiceProvider(dataServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), + http: new PluginServiceProvider(httpServiceFactory), + optionsList: new PluginServiceProvider(optionsListServiceFactory), + overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), - unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), theme: new PluginServiceProvider(themeServiceFactory), - - controls: new PluginServiceProvider(controlsServiceFactory), - optionsList: new PluginServiceProvider(optionsListServiceFactory), + unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; export const pluginServices = new PluginServices(); diff --git a/src/plugins/controls/public/services/plugin_services.ts b/src/plugins/controls/public/services/plugin_services.ts index 4debd0e8c9eb..f1811063e39a 100644 --- a/src/plugins/controls/public/services/plugin_services.ts +++ b/src/plugins/controls/public/services/plugin_services.ts @@ -30,16 +30,15 @@ export const providers: PluginServiceProviders< ControlsServices, KibanaPluginServiceParams > = { - http: new PluginServiceProvider(httpServiceFactory), + controls: new PluginServiceProvider(controlsServiceFactory), data: new PluginServiceProvider(dataServiceFactory), - unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), - overlays: new PluginServiceProvider(overlaysServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), + http: new PluginServiceProvider(httpServiceFactory), + optionsList: new PluginServiceProvider(optionsListServiceFactory, ['data', 'http']), + overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), theme: new PluginServiceProvider(themeServiceFactory), - - optionsList: new PluginServiceProvider(optionsListServiceFactory, ['data', 'http']), - controls: new PluginServiceProvider(controlsServiceFactory), + unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; export const pluginServices = new PluginServices(); diff --git a/src/plugins/dashboard/common/embeddable/embeddable_references.test.ts b/src/plugins/dashboard/common/embeddable/embeddable_references.test.ts deleted file mode 100644 index 3a6475b60251..000000000000 --- a/src/plugins/dashboard/common/embeddable/embeddable_references.test.ts +++ /dev/null @@ -1,76 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { - ExtractDeps, - extractPanelsReferences, - InjectDeps, - injectPanelsReferences, -} from './embeddable_references'; -import { createEmbeddablePersistableStateServiceMock } from '@kbn/embeddable-plugin/common/mocks'; -import { SavedDashboardPanel } from '../types'; -import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; - -const embeddablePersistableStateService = createEmbeddablePersistableStateServiceMock(); -const deps: InjectDeps & ExtractDeps = { - embeddablePersistableStateService, -}; - -test('inject/extract panel references', () => { - embeddablePersistableStateService.extract.mockImplementationOnce((state) => { - const { HARDCODED_ID, ...restOfState } = state as unknown as Record; - return { - state: restOfState as EmbeddableStateWithType, - references: [{ id: HARDCODED_ID as string, name: 'refName', type: 'type' }], - }; - }); - - embeddablePersistableStateService.inject.mockImplementationOnce((state, references) => { - const ref = references.find((r) => r.name === 'refName'); - return { - ...state, - HARDCODED_ID: ref!.id, - }; - }); - - const savedDashboardPanel: SavedDashboardPanel = { - type: 'search', - embeddableConfig: { - HARDCODED_ID: 'IMPORTANT_HARDCODED_ID', - }, - id: 'savedObjectId', - panelIndex: '123', - gridData: { - x: 0, - y: 0, - h: 15, - w: 15, - i: '123', - }, - version: '7.0.0', - }; - - const [{ panel: extractedPanel, references }] = extractPanelsReferences( - [savedDashboardPanel], - deps - ); - expect(extractedPanel.embeddableConfig).toEqual({}); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "IMPORTANT_HARDCODED_ID", - "name": "refName", - "type": "type", - }, - ] - `); - - const [injectedPanel] = injectPanelsReferences([extractedPanel], references, deps); - - expect(injectedPanel).toEqual(savedDashboardPanel); -}); diff --git a/src/plugins/dashboard/common/embeddable/embeddable_references.ts b/src/plugins/dashboard/common/embeddable/embeddable_references.ts deleted file mode 100644 index 6664f70d3392..000000000000 --- a/src/plugins/dashboard/common/embeddable/embeddable_references.ts +++ /dev/null @@ -1,69 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { omit } from 'lodash'; -import { SavedObjectReference } from '@kbn/core/types'; -import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common/types'; -import { - convertSavedDashboardPanelToPanelState, - convertPanelStateToSavedDashboardPanel, -} from './embeddable_saved_object_converters'; -import { SavedDashboardPanel } from '../types'; - -export interface InjectDeps { - embeddablePersistableStateService: EmbeddablePersistableStateService; -} - -export function injectPanelsReferences( - panels: SavedDashboardPanel[], - references: SavedObjectReference[], - deps: InjectDeps -): SavedDashboardPanel[] { - const result: SavedDashboardPanel[] = []; - for (const panel of panels) { - const embeddableState = convertSavedDashboardPanelToPanelState(panel); - embeddableState.explicitInput = omit( - deps.embeddablePersistableStateService.inject( - { ...embeddableState.explicitInput, type: panel.type }, - references - ), - 'type' - ); - result.push(convertPanelStateToSavedDashboardPanel(embeddableState, panel.version)); - } - return result; -} - -export interface ExtractDeps { - embeddablePersistableStateService: EmbeddablePersistableStateService; -} - -export function extractPanelsReferences( - panels: SavedDashboardPanel[], - deps: ExtractDeps -): Array<{ panel: SavedDashboardPanel; references: SavedObjectReference[] }> { - const result: Array<{ panel: SavedDashboardPanel; references: SavedObjectReference[] }> = []; - - for (const panel of panels) { - const embeddable = convertSavedDashboardPanelToPanelState(panel); - const { state: embeddableInputWithExtractedReferences, references } = - deps.embeddablePersistableStateService.extract({ - ...embeddable.explicitInput, - type: embeddable.type, - }); - embeddable.explicitInput = omit(embeddableInputWithExtractedReferences, 'type'); - - const newPanel = convertPanelStateToSavedDashboardPanel(embeddable, panel.version); - result.push({ - panel: newPanel, - references, - }); - } - - return result; -} diff --git a/src/plugins/dashboard/common/embeddable/types.ts b/src/plugins/dashboard/common/embeddable/types.ts deleted file mode 100644 index d786078766f7..000000000000 --- a/src/plugins/dashboard/common/embeddable/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type GridData = { - w: number; - h: number; - x: number; - y: number; - i: string; -}; diff --git a/src/plugins/dashboard/common/index.ts b/src/plugins/dashboard/common/index.ts index 73e01693977d..81833f8a8f18 100644 --- a/src/plugins/dashboard/common/index.ts +++ b/src/plugins/dashboard/common/index.ts @@ -6,24 +6,28 @@ * Side Public License, v 1. */ -export type { GridData } from './embeddable/types'; -export type { - RawSavedDashboardPanel730ToLatest, - DashboardDoc730ToLatest, - DashboardDoc700To720, - DashboardDocPre700, -} from './bwc/types'; export type { + GridData, + DashboardPanelMap, + SavedDashboardPanel, + DashboardAttributes, + DashboardPanelState, DashboardContainerStateWithType, - SavedDashboardPanelTo60, - SavedDashboardPanel610, - SavedDashboardPanel620, - SavedDashboardPanel630, - SavedDashboardPanel640To720, - SavedDashboardPanel730ToLatest, } from './types'; -export { migratePanelsTo730 } from './migrate_to_730_panels'; +export { + injectReferences, + extractReferences, +} from './persistable_state/dashboard_saved_object_references'; + +export { createInject, createExtract } from './persistable_state/dashboard_container_references'; + +export { + convertPanelStateToSavedDashboardPanel, + convertSavedDashboardPanelToPanelState, + convertSavedPanelsToPanelMap, + convertPanelMapToSavedPanels, +} from './lib/dashboard_panel_converters'; export const UI_SETTINGS = { ENABLE_LABS_UI: 'labs:dashboard:enable_ui', diff --git a/src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.test.ts b/src/plugins/dashboard/common/lib/dashboard_panel_converters.test.ts similarity index 98% rename from src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.test.ts rename to src/plugins/dashboard/common/lib/dashboard_panel_converters.test.ts index 9ec93fa85fc5..2ebca116f3f1 100644 --- a/src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.test.ts +++ b/src/plugins/dashboard/common/lib/dashboard_panel_converters.test.ts @@ -9,7 +9,7 @@ import { convertSavedDashboardPanelToPanelState, convertPanelStateToSavedDashboardPanel, -} from './embeddable_saved_object_converters'; +} from './dashboard_panel_converters'; import { SavedDashboardPanel, DashboardPanelState } from '../types'; import { EmbeddableInput } from '@kbn/embeddable-plugin/common/types'; diff --git a/src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.ts b/src/plugins/dashboard/common/lib/dashboard_panel_converters.ts similarity index 75% rename from src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.ts rename to src/plugins/dashboard/common/lib/dashboard_panel_converters.ts index aa9519a5a48b..2652c7f9a40a 100644 --- a/src/plugins/dashboard/common/embeddable/embeddable_saved_object_converters.ts +++ b/src/plugins/dashboard/common/lib/dashboard_panel_converters.ts @@ -8,7 +8,7 @@ import { omit } from 'lodash'; import { EmbeddableInput, SavedObjectEmbeddableInput } from '@kbn/embeddable-plugin/common'; -import { DashboardPanelState, SavedDashboardPanel } from '../types'; +import { DashboardPanelMap, DashboardPanelState, SavedDashboardPanel } from '../types'; export function convertSavedDashboardPanelToPanelState< TEmbeddableInput extends EmbeddableInput | SavedObjectEmbeddableInput = SavedObjectEmbeddableInput @@ -42,3 +42,17 @@ export function convertPanelStateToSavedDashboardPanel( ...(panelState.panelRefName !== undefined && { panelRefName: panelState.panelRefName }), }; } + +export const convertSavedPanelsToPanelMap = (panels?: SavedDashboardPanel[]): DashboardPanelMap => { + const panelsMap: DashboardPanelMap = {}; + panels?.forEach((panel, idx) => { + panelsMap![panel.panelIndex ?? String(idx)] = convertSavedDashboardPanelToPanelState(panel); + }); + return panelsMap; +}; + +export const convertPanelMapToSavedPanels = (panels: DashboardPanelMap, version: string) => { + return Object.values(panels).map((panel) => + convertPanelStateToSavedDashboardPanel(panel, version) + ); +}; diff --git a/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.test.ts b/src/plugins/dashboard/common/persistable_state/dashboard_container_references.test.ts similarity index 99% rename from src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.test.ts rename to src/plugins/dashboard/common/persistable_state/dashboard_container_references.test.ts index ee13926486f8..47215e5e3200 100644 --- a/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.test.ts +++ b/src/plugins/dashboard/common/persistable_state/dashboard_container_references.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { createExtract, createInject } from './dashboard_container_persistable_state'; +import { createExtract, createInject } from './dashboard_container_references'; import { createEmbeddablePersistableStateServiceMock } from '@kbn/embeddable-plugin/common/mocks'; import { DashboardContainerStateWithType } from '../types'; diff --git a/src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts b/src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts similarity index 100% rename from src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts rename to src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts diff --git a/src/plugins/dashboard/common/saved_dashboard_references.test.ts b/src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.test.ts similarity index 98% rename from src/plugins/dashboard/common/saved_dashboard_references.test.ts rename to src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.test.ts index 84bfe6ea48d3..e28a429d6d00 100644 --- a/src/plugins/dashboard/common/saved_dashboard_references.test.ts +++ b/src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.test.ts @@ -11,9 +11,9 @@ import { injectReferences, InjectDeps, ExtractDeps, -} from './saved_dashboard_references'; +} from './dashboard_saved_object_references'; -import { createExtract, createInject } from './embeddable/dashboard_container_persistable_state'; +import { createExtract, createInject } from './dashboard_container_references'; import { createEmbeddablePersistableStateServiceMock } from '@kbn/embeddable-plugin/common/mocks'; const embeddablePersistableStateServiceMock = createEmbeddablePersistableStateServiceMock(); diff --git a/src/plugins/dashboard/common/saved_dashboard_references.ts b/src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts similarity index 96% rename from src/plugins/dashboard/common/saved_dashboard_references.ts rename to src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts index e3a3193dd85a..a3126a381d94 100644 --- a/src/plugins/dashboard/common/saved_dashboard_references.ts +++ b/src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts @@ -6,23 +6,33 @@ * Side Public License, v 1. */ import semverGt from 'semver/functions/gt'; -import { SavedObjectAttributes, SavedObjectReference } from '@kbn/core/types'; -import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common/types'; + import { - PersistableControlGroupInput, RawControlGroupAttributes, + PersistableControlGroupInput, } from '@kbn/controls-plugin/common'; -import { DashboardContainerStateWithType, DashboardPanelState } from './types'; +import { SavedObjectAttributes, SavedObjectReference } from '@kbn/core/types'; +import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common/types'; + +import { + SavedDashboardPanel, + DashboardPanelState, + DashboardContainerStateWithType, +} from '../types'; import { convertPanelStateToSavedDashboardPanel, convertSavedDashboardPanelToPanelState, -} from './embeddable/embeddable_saved_object_converters'; -import { SavedDashboardPanel } from './types'; +} from '../lib/dashboard_panel_converters'; export interface ExtractDeps { embeddablePersistableStateService: EmbeddablePersistableStateService; } -export interface SavedObjectAttributesAndReferences { + +export interface InjectDeps { + embeddablePersistableStateService: EmbeddablePersistableStateService; +} + +interface SavedObjectAttributesAndReferences { attributes: SavedObjectAttributes; references: SavedObjectReference[]; } @@ -79,7 +89,7 @@ function panelStatesToPanels( let originalPanel = originalPanels.find((p) => p.panelIndex === id); if (!originalPanel) { - // Maybe original panel doesn't have a panel index and it's just straight up based on it's index + // Maybe original panel doesn't have a panel index and it's just straight up based on its index const numericId = parseInt(id, 10); originalPanel = isNaN(numericId) ? originalPanel : originalPanels[numericId]; } @@ -91,6 +101,45 @@ function panelStatesToPanels( }); } +export function injectReferences( + { attributes, references = [] }: SavedObjectAttributesAndReferences, + deps: InjectDeps +): SavedObjectAttributes { + // Skip if panelsJSON is missing otherwise this will cause saved object import to fail when + // importing objects without panelsJSON. At development time of this, there is no guarantee each saved + // object has panelsJSON in all previous versions of kibana. + if (typeof attributes.panelsJSON !== 'string') { + return attributes; + } + const parsedPanels = JSON.parse(attributes.panelsJSON); + // Same here, prevent failing saved object import if ever panels aren't an array. + if (!Array.isArray(parsedPanels)) { + return attributes; + } + + const { panels, state } = dashboardAttributesToState(attributes); + + const injectedState = deps.embeddablePersistableStateService.inject( + state, + references + ) as DashboardContainerStateWithType; + const injectedPanels = panelStatesToPanels(injectedState.panels, panels); + + const newAttributes = { + ...attributes, + panelsJSON: JSON.stringify(injectedPanels), + } as SavedObjectAttributes; + + if (injectedState.controlGroupInput) { + newAttributes.controlGroupInput = { + ...(attributes.controlGroupInput as SavedObjectAttributes), + panelsJSON: JSON.stringify(injectedState.controlGroupInput.panels), + }; + } + + return newAttributes; +} + export function extractReferences( { attributes, references = [] }: SavedObjectAttributesAndReferences, deps: ExtractDeps @@ -137,49 +186,6 @@ export function extractReferences( }; } -export interface InjectDeps { - embeddablePersistableStateService: EmbeddablePersistableStateService; -} - -export function injectReferences( - { attributes, references = [] }: SavedObjectAttributesAndReferences, - deps: InjectDeps -): SavedObjectAttributes { - // Skip if panelsJSON is missing otherwise this will cause saved object import to fail when - // importing objects without panelsJSON. At development time of this, there is no guarantee each saved - // object has panelsJSON in all previous versions of kibana. - if (typeof attributes.panelsJSON !== 'string') { - return attributes; - } - const parsedPanels = JSON.parse(attributes.panelsJSON); - // Same here, prevent failing saved object import if ever panels aren't an array. - if (!Array.isArray(parsedPanels)) { - return attributes; - } - - const { panels, state } = dashboardAttributesToState(attributes); - - const injectedState = deps.embeddablePersistableStateService.inject( - state, - references - ) as DashboardContainerStateWithType; - const injectedPanels = panelStatesToPanels(injectedState.panels, panels); - - const newAttributes = { - ...attributes, - panelsJSON: JSON.stringify(injectedPanels), - } as SavedObjectAttributes; - - if (injectedState.controlGroupInput) { - newAttributes.controlGroupInput = { - ...(attributes.controlGroupInput as SavedObjectAttributes), - panelsJSON: JSON.stringify(injectedState.controlGroupInput.panels), - }; - } - - return newAttributes; -} - function pre730ExtractReferences( { attributes, references = [] }: SavedObjectAttributesAndReferences, deps: ExtractDeps diff --git a/src/plugins/dashboard/common/types.ts b/src/plugins/dashboard/common/types.ts index 941f9437e54e..ff5a1cbc1755 100644 --- a/src/plugins/dashboard/common/types.ts +++ b/src/plugins/dashboard/common/types.ts @@ -11,28 +11,13 @@ import { EmbeddableStateWithType, PanelState, } from '@kbn/embeddable-plugin/common/types'; -import { SavedObjectEmbeddableInput } from '@kbn/embeddable-plugin/common/lib/saved_object_embeddable'; -import { PersistableControlGroupInput } from '@kbn/controls-plugin/common'; +import { Serializable } from '@kbn/utility-types'; import { - RawSavedDashboardPanelTo60, - RawSavedDashboardPanel610, - RawSavedDashboardPanel620, - RawSavedDashboardPanel630, - RawSavedDashboardPanel640To720, - RawSavedDashboardPanel730ToLatest, -} from './bwc/types'; - -import { GridData } from './embeddable/types'; - -export type PanelId = string; -export type SavedObjectId = string; - -export interface DashboardPanelState< - TEmbeddableInput extends EmbeddableInput | SavedObjectEmbeddableInput = SavedObjectEmbeddableInput -> extends PanelState { - readonly gridData: GridData; - panelRefName?: string; -} + PersistableControlGroupInput, + RawControlGroupAttributes, +} from '@kbn/controls-plugin/common'; +import { RefreshInterval } from '@kbn/data-plugin/common'; +import { SavedObjectEmbeddableInput } from '@kbn/embeddable-plugin/common/lib/saved_object_embeddable'; export interface DashboardCapabilities { showWriteControls: boolean; @@ -43,62 +28,77 @@ export interface DashboardCapabilities { } /** - * This should always represent the latest dashboard panel shape, after all possible migrations. + * The attributes of the dashboard saved object. This interface should be the + * source of truth for the latest dashboard attributes shape after all migrations. */ -export type SavedDashboardPanel = SavedDashboardPanel730ToLatest; - -export type SavedDashboardPanel640To720 = Pick< - RawSavedDashboardPanel640To720, - Exclude -> & { - readonly id: string; - readonly type: string; -}; +export interface DashboardAttributes { + controlGroupInput?: RawControlGroupAttributes; + refreshInterval?: RefreshInterval; + timeRestore: boolean; + optionsJSON?: string; + useMargins?: boolean; + description: string; + panelsJSON: string; + timeFrom?: string; + version: number; + timeTo?: string; + title: string; + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; +} -export type SavedDashboardPanel630 = Pick< - RawSavedDashboardPanel630, - Exclude -> & { - readonly id: string; - readonly type: string; -}; +/** -------------------------------------------------------------------- + * Dashboard panel types + -----------------------------------------------------------------------*/ -export type SavedDashboardPanel620 = Pick< - RawSavedDashboardPanel620, - Exclude -> & { - readonly id: string; - readonly type: string; -}; +/** + * The dashboard panel format expected by the embeddable container. + */ +export interface DashboardPanelState< + TEmbeddableInput extends EmbeddableInput | SavedObjectEmbeddableInput = SavedObjectEmbeddableInput +> extends PanelState { + readonly gridData: GridData; + panelRefName?: string; +} -export type SavedDashboardPanel610 = Pick< - RawSavedDashboardPanel610, - Exclude -> & { - readonly id: string; - readonly type: string; -}; +/** + * A saved dashboard panel parsed directly from the Dashboard Attributes panels JSON + */ +export interface SavedDashboardPanel { + embeddableConfig: { [key: string]: Serializable }; // parsed into the panel's explicitInput + id?: string; // the saved object id for by reference panels + type: string; // the embeddable type + panelRefName?: string; + gridData: GridData; + panelIndex: string; + version: string; + title?: string; +} -export type SavedDashboardPanelTo60 = Pick< - RawSavedDashboardPanelTo60, - Exclude -> & { - readonly id: string; - readonly type: string; -}; +/** + * Grid type for React Grid Layout + */ +export interface GridData { + w: number; + h: number; + x: number; + y: number; + i: string; +} -// id becomes optional starting in 7.3.0 -export type SavedDashboardPanel730ToLatest = Pick< - RawSavedDashboardPanel730ToLatest, - Exclude -> & { - readonly id?: string; - readonly type: string; -}; +export interface DashboardPanelMap { + [key: string]: DashboardPanelState; +} -// Making this interface because so much of the Container type from embeddable is tied up in public -// Once that is all available from common, we should be able to move the dashboard_container type to our common as well +/** -------------------------------------------------------------------- + * Dashboard container types + -----------------------------------------------------------------------*/ +/** + * Types below this line are copied here because so many important types are tied up in public. These types should be + * moved from public into common. + */ export interface DashboardContainerStateWithType extends EmbeddableStateWithType { panels: { [panelId: string]: DashboardPanelState; diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx index ac467e35729f..aa3419e37890 100644 --- a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx +++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { AddToLibraryAction } from '.'; import { DashboardContainer } from '../embeddable/dashboard_container'; import { getSampleDashboardInput } from '../test_helpers'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; @@ -27,6 +26,7 @@ import { CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { pluginServices } from '../../services/plugin_services'; +import { AddToLibraryAction } from './add_to_library_action'; const embeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); pluginServices.getServices().embeddable.getEmbeddableFactory = jest diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx index 8c6577012161..0510d35519ff 100644 --- a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx +++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx @@ -18,8 +18,9 @@ import { import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { dashboardAddToLibraryAction } from '../../dashboard_strings'; -import { type DashboardPanelState, DASHBOARD_CONTAINER_TYPE, type DashboardContainer } from '..'; +import { type DashboardPanelState, type DashboardContainer } from '..'; import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; export const ACTION_ADD_TO_LIBRARY = 'saveToLibrary'; diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx index 5bb331e58fe3..0fb63049ebe3 100644 --- a/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx +++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx @@ -12,7 +12,7 @@ import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helper import { coreMock } from '@kbn/core/public/mocks'; import { CoreStart } from '@kbn/core/public'; -import { ClonePanelAction } from '.'; +import { ClonePanelAction } from './clone_panel_action'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { ContactCardEmbeddable, diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx index 02862e7c75e8..11a96733337f 100644 --- a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx @@ -27,9 +27,10 @@ import { placePanelBeside, IPanelPlacementBesideArgs, } from '../embeddable/panel/dashboard_panel_placement'; -import { dashboardClonePanelAction } from '../../dashboard_strings'; -import { type DashboardPanelState, DASHBOARD_CONTAINER_TYPE, type DashboardContainer } from '..'; import { pluginServices } from '../../services/plugin_services'; +import { dashboardClonePanelAction } from '../../dashboard_strings'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; +import { type DashboardPanelState, type DashboardContainer } from '..'; export const ACTION_CLONE_PANEL = 'clonePanel'; diff --git a/src/plugins/dashboard/public/application/actions/copy_to_dashboard_action.tsx b/src/plugins/dashboard/public/application/actions/copy_to_dashboard_action.tsx index 8f602db5e452..cdd8d726e9fa 100644 --- a/src/plugins/dashboard/public/application/actions/copy_to_dashboard_action.tsx +++ b/src/plugins/dashboard/public/application/actions/copy_to_dashboard_action.tsx @@ -14,9 +14,10 @@ import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { dashboardCopyToDashboardAction } from '../../dashboard_strings'; -import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; +import { DashboardContainer } from '../embeddable'; import { CopyToDashboardModal } from './copy_to_dashboard_modal'; import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; export const ACTION_COPY_TO_DASHBOARD = 'copyToDashboard'; diff --git a/src/plugins/dashboard/public/application/actions/copy_to_dashboard_modal.tsx b/src/plugins/dashboard/public/application/actions/copy_to_dashboard_modal.tsx index 7f9a99ed2723..af91631d20b3 100644 --- a/src/plugins/dashboard/public/application/actions/copy_to_dashboard_modal.tsx +++ b/src/plugins/dashboard/public/application/actions/copy_to_dashboard_modal.tsx @@ -25,8 +25,8 @@ import { import { IEmbeddable, PanelNotFoundError } from '@kbn/embeddable-plugin/public'; import { LazyDashboardPicker, withSuspense } from '@kbn/presentation-util-plugin/public'; import { dashboardCopyToDashboardAction } from '../../dashboard_strings'; -import { createDashboardEditUrl, DashboardConstants, DashboardContainer } from '../..'; -import { DashboardPanelState } from '..'; +import { createDashboardEditUrl, DashboardConstants } from '../..'; +import { type DashboardContainer, DashboardPanelState } from '..'; import { pluginServices } from '../../services/plugin_services'; interface CopyToDashboardModalProps { diff --git a/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx b/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx index c5da566b90a6..79ab109ddce5 100644 --- a/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx @@ -9,9 +9,9 @@ import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import type { DashboardContainerInput } from '../..'; +import { DashboardContainerInput, DASHBOARD_CONTAINER_TYPE } from '../..'; import { dashboardExpandPanelAction } from '../../dashboard_strings'; -import { DASHBOARD_CONTAINER_TYPE, type DashboardContainer } from '../embeddable'; +import { type DashboardContainer } from '../embeddable'; export const ACTION_EXPAND_PANEL = 'togglePanel'; diff --git a/src/plugins/dashboard/public/application/actions/filters_notification_badge.test.tsx b/src/plugins/dashboard/public/application/actions/filters_notification_badge.test.tsx index 275a5625e5e0..3b3fb5dde049 100644 --- a/src/plugins/dashboard/public/application/actions/filters_notification_badge.test.tsx +++ b/src/plugins/dashboard/public/application/actions/filters_notification_badge.test.tsx @@ -6,27 +6,26 @@ * Side Public License, v 1. */ -import { getSampleDashboardInput } from '../test_helpers'; -import { DashboardContainer } from '../embeddable/dashboard_container'; - -import { FiltersNotificationBadge } from '.'; -import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { type Query, type AggregateQuery, Filter } from '@kbn/es-query'; import { - ErrorEmbeddable, - FilterableEmbeddable, IContainer, + ErrorEmbeddable, isErrorEmbeddable, + FilterableEmbeddable, } from '@kbn/embeddable-plugin/public'; - import { ContactCardEmbeddable, - ContactCardEmbeddableFactory, + CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, - CONTACT_CARD_EMBEDDABLE, + ContactCardEmbeddableFactory, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; +import { type Query, type AggregateQuery, Filter } from '@kbn/es-query'; +import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; + +import { getSampleDashboardInput } from '../test_helpers'; import { pluginServices } from '../../services/plugin_services'; +import { DashboardContainer } from '../embeddable/dashboard_container'; +import { FiltersNotificationBadge } from './filters_notification_badge'; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); pluginServices.getServices().embeddable.getEmbeddableFactory = jest diff --git a/src/plugins/dashboard/public/application/actions/index.ts b/src/plugins/dashboard/public/application/actions/index.ts index 5c95e3c42ccc..a238ce05e101 100644 --- a/src/plugins/dashboard/public/application/actions/index.ts +++ b/src/plugins/dashboard/public/application/actions/index.ts @@ -6,23 +6,76 @@ * Side Public License, v 1. */ -export type { ExpandPanelActionContext } from './expand_panel_action'; -export { ExpandPanelAction, ACTION_EXPAND_PANEL } from './expand_panel_action'; -export type { ReplacePanelActionContext } from './replace_panel_action'; -export { ReplacePanelAction, ACTION_REPLACE_PANEL } from './replace_panel_action'; -export type { ClonePanelActionContext } from './clone_panel_action'; -export { ClonePanelAction, ACTION_CLONE_PANEL } from './clone_panel_action'; -export type { AddToLibraryActionContext } from './add_to_library_action'; -export { AddToLibraryAction, ACTION_ADD_TO_LIBRARY } from './add_to_library_action'; -export type { UnlinkFromLibraryActionContext } from './unlink_from_library_action'; -export { UnlinkFromLibraryAction, ACTION_UNLINK_FROM_LIBRARY } from './unlink_from_library_action'; -export type { CopyToDashboardActionContext } from './copy_to_dashboard_action'; -export { CopyToDashboardAction, ACTION_COPY_TO_DASHBOARD } from './copy_to_dashboard_action'; -export type { LibraryNotificationActionContext } from './library_notification_action'; -export { - LibraryNotificationAction, - ACTION_LIBRARY_NOTIFICATION, -} from './library_notification_action'; -export { FiltersNotificationBadge, BADGE_FILTERS_NOTIFICATION } from './filters_notification_badge'; -export type { ExportContext } from './export_csv_action'; -export { ExportCSVAction, ACTION_EXPORT_CSV } from './export_csv_action'; +import { + CONTEXT_MENU_TRIGGER, + PANEL_BADGE_TRIGGER, + PANEL_NOTIFICATION_TRIGGER, +} from '@kbn/embeddable-plugin/public'; +import { CoreStart } from '@kbn/core/public'; +import { getSavedObjectFinder } from '@kbn/saved-objects-plugin/public'; + +import { ExportCSVAction } from './export_csv_action'; +import { ClonePanelAction } from './clone_panel_action'; +import { DashboardStartDependencies } from '../../plugin'; +import { ExpandPanelAction } from './expand_panel_action'; +import { ReplacePanelAction } from './replace_panel_action'; +import { AddToLibraryAction } from './add_to_library_action'; +import { CopyToDashboardAction } from './copy_to_dashboard_action'; +import { UnlinkFromLibraryAction } from './unlink_from_library_action'; +import { FiltersNotificationBadge } from './filters_notification_badge'; +import { LibraryNotificationAction } from './library_notification_action'; + +interface BuildAllDashboardActionsProps { + core: CoreStart; + allowByValueEmbeddables?: boolean; + plugins: DashboardStartDependencies; +} + +export const buildAllDashboardActions = async ({ + core, + plugins, + allowByValueEmbeddables, +}: BuildAllDashboardActionsProps) => { + const { uiSettings } = core; + const { uiActions, share, presentationUtil } = plugins; + + const clonePanelAction = new ClonePanelAction(core.savedObjects); + uiActions.registerAction(clonePanelAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id); + + const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, uiSettings); + const changeViewAction = new ReplacePanelAction(SavedObjectFinder); + uiActions.registerAction(changeViewAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction.id); + + const panelLevelFiltersNotification = new FiltersNotificationBadge(); + uiActions.registerAction(panelLevelFiltersNotification); + uiActions.attachAction(PANEL_BADGE_TRIGGER, panelLevelFiltersNotification.id); + + const expandPanelAction = new ExpandPanelAction(); + uiActions.registerAction(expandPanelAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id); + + if (share) { + const ExportCSVPlugin = new ExportCSVAction(); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, ExportCSVPlugin); + } + + if (allowByValueEmbeddables) { + const addToLibraryAction = new AddToLibraryAction(); + uiActions.registerAction(addToLibraryAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, addToLibraryAction.id); + + const unlinkFromLibraryAction = new UnlinkFromLibraryAction(); + uiActions.registerAction(unlinkFromLibraryAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, unlinkFromLibraryAction.id); + + const libraryNotificationAction = new LibraryNotificationAction(unlinkFromLibraryAction); + uiActions.registerAction(libraryNotificationAction); + uiActions.attachAction(PANEL_NOTIFICATION_TRIGGER, libraryNotificationAction.id); + + const copyToDashboardAction = new CopyToDashboardAction(presentationUtil.ContextProvider); + uiActions.registerAction(copyToDashboardAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, copyToDashboardAction.id); + } +}; diff --git a/src/plugins/dashboard/public/application/actions/library_notification_action.test.tsx b/src/plugins/dashboard/public/application/actions/library_notification_action.test.tsx index f1202de4ac1b..f30cba538b8d 100644 --- a/src/plugins/dashboard/public/application/actions/library_notification_action.test.tsx +++ b/src/plugins/dashboard/public/application/actions/library_notification_action.test.tsx @@ -6,11 +6,6 @@ * Side Public License, v 1. */ -import { getSampleDashboardInput } from '../test_helpers'; -import { DashboardContainer } from '../embeddable/dashboard_container'; - -import { LibraryNotificationAction, UnlinkFromLibraryAction } from '.'; -import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { ErrorEmbeddable, IContainer, @@ -25,7 +20,13 @@ import { ContactCardEmbeddableOutput, CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; +import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; + +import { getSampleDashboardInput } from '../test_helpers'; import { pluginServices } from '../../services/plugin_services'; +import { DashboardContainer } from '../embeddable/dashboard_container'; +import { UnlinkFromLibraryAction } from './unlink_from_library_action'; +import { LibraryNotificationAction } from './library_notification_action'; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); pluginServices.getServices().embeddable.getEmbeddableFactory = jest diff --git a/src/plugins/dashboard/public/application/actions/library_notification_action.tsx b/src/plugins/dashboard/public/application/actions/library_notification_action.tsx index a5abe8161e9a..a05b78994b31 100644 --- a/src/plugins/dashboard/public/application/actions/library_notification_action.tsx +++ b/src/plugins/dashboard/public/application/actions/library_notification_action.tsx @@ -17,10 +17,10 @@ import { import { KibanaThemeProvider, reactToUiComponent } from '@kbn/kibana-react-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { UnlinkFromLibraryAction } from '.'; -import { LibraryNotificationPopover } from './library_notification_popover'; -import { dashboardLibraryNotification } from '../../dashboard_strings'; import { pluginServices } from '../../services/plugin_services'; +import { UnlinkFromLibraryAction } from './unlink_from_library_action'; +import { dashboardLibraryNotification } from '../../dashboard_strings'; +import { LibraryNotificationPopover } from './library_notification_popover'; export const ACTION_LIBRARY_NOTIFICATION = 'ACTION_LIBRARY_NOTIFICATION'; diff --git a/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx b/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx index 81d2f2a0557e..38c8452eadde 100644 --- a/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx +++ b/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx @@ -17,8 +17,10 @@ import { EuiPopoverTitle, EuiText, } from '@elastic/eui'; -import { LibraryNotificationActionContext, UnlinkFromLibraryAction } from '.'; + import { dashboardLibraryNotification } from '../../dashboard_strings'; +import { UnlinkFromLibraryAction } from './unlink_from_library_action'; +import { LibraryNotificationActionContext } from './library_notification_action'; export interface LibraryNotificationProps { context: LibraryNotificationActionContext; diff --git a/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx b/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx index f39988842e3f..52f6a345a181 100644 --- a/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/replace_panel_action.tsx @@ -8,9 +8,10 @@ import { type IEmbeddable, ViewMode } from '@kbn/embeddable-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { DASHBOARD_CONTAINER_TYPE, type DashboardContainer } from '../embeddable'; +import type { DashboardContainer } from '../embeddable'; import { openReplacePanelFlyout } from './open_replace_panel_flyout'; import { dashboardReplacePanelAction } from '../../dashboard_strings'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; export const ACTION_REPLACE_PANEL = 'replacePanel'; diff --git a/src/plugins/dashboard/public/application/actions/unlink_from_library_action.test.tsx b/src/plugins/dashboard/public/application/actions/unlink_from_library_action.test.tsx index 854383edd4e1..080c358a86fd 100644 --- a/src/plugins/dashboard/public/application/actions/unlink_from_library_action.test.tsx +++ b/src/plugins/dashboard/public/application/actions/unlink_from_library_action.test.tsx @@ -23,10 +23,10 @@ import { CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; -import { UnlinkFromLibraryAction } from '.'; import { getSampleDashboardInput } from '../test_helpers'; -import { DashboardContainer } from '../embeddable/dashboard_container'; import { pluginServices } from '../../services/plugin_services'; +import { UnlinkFromLibraryAction } from './unlink_from_library_action'; +import { DashboardContainer } from '../embeddable/dashboard_container'; let container: DashboardContainer; let embeddable: ContactCardEmbeddable & ReferenceOrValueEmbeddable; diff --git a/src/plugins/dashboard/public/application/actions/unlink_from_library_action.tsx b/src/plugins/dashboard/public/application/actions/unlink_from_library_action.tsx index e399411e77fe..b7c53a78becc 100644 --- a/src/plugins/dashboard/public/application/actions/unlink_from_library_action.tsx +++ b/src/plugins/dashboard/public/application/actions/unlink_from_library_action.tsx @@ -17,8 +17,9 @@ import { } from '@kbn/embeddable-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { dashboardUnlinkFromLibraryAction } from '../../dashboard_strings'; -import { type DashboardPanelState, DASHBOARD_CONTAINER_TYPE, type DashboardContainer } from '..'; +import { type DashboardPanelState, type DashboardContainer } from '..'; import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; export const ACTION_UNLINK_FROM_LIBRARY = 'unlinkFromLibrary'; diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx index 302cb4379422..b05944c99292 100644 --- a/src/plugins/dashboard/public/application/dashboard_app.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app.tsx @@ -9,24 +9,23 @@ import { History } from 'history'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { EmbeddableRenderer, ViewMode } from '@kbn/embeddable-plugin/public'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; +import { EmbeddableRenderer, ViewMode } from '@kbn/embeddable-plugin/public'; import { createKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; -import { useDashboardSelector } from './state'; -import { useDashboardAppState } from './hooks'; import { dashboardFeatureCatalog, getDashboardBreadcrumb, getDashboardTitle, leaveConfirmStrings, } from '../dashboard_strings'; -import { createDashboardEditUrl } from '../dashboard_constants'; -import { DashboardTopNav, isCompleteDashboardAppState } from './top_nav/dashboard_top_nav'; -import { DashboardEmbedSettings, DashboardRedirect } from '../types'; -import { DashboardAppNoDataPage } from './dashboard_app_no_data'; +import { useDashboardAppState } from './hooks'; +import { useDashboardSelector } from './state'; import { pluginServices } from '../services/plugin_services'; +import { DashboardAppNoDataPage } from './dashboard_app_no_data'; +import { DashboardEmbedSettings, DashboardRedirect } from '../types'; import { useDashboardMountContext } from './hooks/dashboard_mount_context'; +import { DashboardTopNav, isCompleteDashboardAppState } from './top_nav/dashboard_top_nav'; export interface DashboardAppProps { history: History; savedDashboardId?: string; @@ -43,13 +42,12 @@ export function DashboardApp({ const { onAppLeave } = useDashboardMountContext(); const { chrome: { setBreadcrumbs, setIsVisible }, + screenshotMode: { isScreenshotMode }, coreContext: { executionContext }, - data: { search }, embeddable: { getStateTransfer }, notifications: { toasts }, - screenshotMode: { isScreenshotMode }, settings: { uiSettings }, - spaces: { getLegacyUrlConflict }, + data: { search }, } = pluginServices.getServices(); const [showNoDataPage, setShowNoDataPage] = useState(false); @@ -160,17 +158,7 @@ export function DashboardApp({ dashboardAppState={dashboardAppState} /> - {dashboardAppState.savedDashboard.outcome === 'conflict' && - dashboardAppState.savedDashboard.id && - dashboardAppState.savedDashboard.aliasId - ? getLegacyUrlConflict?.({ - currentObjectId: dashboardAppState.savedDashboard.id, - otherObjectId: dashboardAppState.savedDashboard.aliasId, - otherObjectPath: `#${createDashboardEditUrl( - dashboardAppState.savedDashboard.aliasId - )}${history.location.search}`, - }) - : null} + {dashboardAppState.createConflictWarning?.()}
    createKbnUrlStateStorage({ history, @@ -172,51 +162,48 @@ export async function mountApp({ core, element, appUnMounted, mountContext }: Da }); const app = ( - // TODO: Remove KibanaContextProvider as part of https://github.com/elastic/kibana/pull/138774 - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + ); diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_constants.ts b/src/plugins/dashboard/public/application/embeddable/dashboard_constants.ts deleted file mode 100644 index 2f7854e81ad9..000000000000 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const DASHBOARD_GRID_COLUMN_COUNT = 48; -export const DASHBOARD_GRID_HEIGHT = 20; -export const DEFAULT_PANEL_WIDTH = DASHBOARD_GRID_COLUMN_COUNT / 2; -export const DEFAULT_PANEL_HEIGHT = 15; -export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 4f7483cf06f3..036c77fc6257 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -32,7 +32,7 @@ import type { Query } from '@kbn/es-query'; import type { RefreshInterval } from '@kbn/data-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; import { createPanelState } from './panel'; import { DashboardPanelState } from './types'; import { DashboardViewport } from './viewport/dashboard_viewport'; diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx index 27670ee10436..58a2c63492c0 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx @@ -7,16 +7,14 @@ */ import { i18n } from '@kbn/i18n'; -import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; - import { identity, pickBy } from 'lodash'; + import { ControlGroupContainer, ControlGroupInput, ControlGroupOutput, CONTROL_GROUP_TYPE, } from '@kbn/controls-plugin/public'; -import { getDefaultControlGroupInput } from '@kbn/controls-plugin/common'; import { Container, ErrorEmbeddable, @@ -25,14 +23,13 @@ import { EmbeddableFactoryDefinition, } from '@kbn/embeddable-plugin/public'; +import { getDefaultControlGroupInput } from '@kbn/controls-plugin/common'; +import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; + import { DashboardContainerInput } from '../..'; -import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; +import { createExtract, createInject } from '../../../common'; import type { DashboardContainer } from './dashboard_container'; -import { - createExtract, - createInject, -} from '../../../common/embeddable/dashboard_container_persistable_state'; -import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; export type DashboardContainerFactory = EmbeddableFactory< DashboardContainerInput, @@ -80,6 +77,7 @@ export class DashboardContainerFactoryDefinition initialInput: DashboardContainerInput, parent?: Container ): Promise => { + const { pluginServices } = await import('../../services/plugin_services'); const { embeddable: { getEmbeddableFactory }, } = pluginServices.getServices(); diff --git a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx index 64afdcdb2e60..7fda6eb1a3f3 100644 --- a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx @@ -22,9 +22,9 @@ import { DashboardContainer, DashboardLoadedInfo } from '../dashboard_container' import { GridData } from '../../../../common'; import { DashboardGridItem } from './dashboard_grid_item'; import { DashboardLoadedEventStatus, DashboardPanelState } from '../types'; -import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../dashboard_constants'; +import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../../../dashboard_constants'; import { pluginServices } from '../../../services/plugin_services'; -import { dashboardLoadingErrorStrings } from '../../../dashboard_strings'; +import { dashboardSavedObjectErrorStrings } from '../../../dashboard_strings'; let lastValidGridSize = 0; @@ -153,7 +153,7 @@ class DashboardGridUi extends React.Component { } catch (error) { console.error(error); // eslint-disable-line no-console isLayoutInvalid = true; - toasts.addDanger(dashboardLoadingErrorStrings.getDashboardGridError(error.message)); + toasts.addDanger(dashboardSavedObjectErrorStrings.getDashboardGridError(error.message)); } this.setState({ layout, diff --git a/src/plugins/dashboard/public/application/embeddable/index.ts b/src/plugins/dashboard/public/application/embeddable/index.ts index ce8bb5b7169a..1979ae5ad7bf 100644 --- a/src/plugins/dashboard/public/application/embeddable/index.ts +++ b/src/plugins/dashboard/public/application/embeddable/index.ts @@ -13,11 +13,4 @@ export { createPanelState } from './panel'; export * from './types'; -export { - DASHBOARD_GRID_COLUMN_COUNT, - DEFAULT_PANEL_HEIGHT, - DEFAULT_PANEL_WIDTH, - DASHBOARD_CONTAINER_TYPE, -} from './dashboard_constants'; - export { createDashboardContainerByValueRenderer } from './dashboard_container_by_value_renderer'; diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts index 10c3044ea912..4c926675e1e9 100644 --- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts +++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts @@ -8,7 +8,7 @@ import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_samples'; -import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants'; +import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../../../dashboard_constants'; import { DashboardPanelState } from '../types'; import { createPanelState } from './create_panel_state'; diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts index 5aa9066ea1eb..e5d4f69c914c 100644 --- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts +++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts @@ -7,7 +7,7 @@ */ import { PanelState, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants'; +import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../../../dashboard_constants'; import { DashboardPanelState } from '../types'; import { IPanelPlacementArgs, diff --git a/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts index 9d90b711a684..77b51874319b 100644 --- a/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts +++ b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts @@ -8,8 +8,8 @@ import _ from 'lodash'; import { PanelNotFoundError } from '@kbn/embeddable-plugin/public'; -import { GridData } from '../../../../common'; -import { DashboardPanelState, DASHBOARD_GRID_COLUMN_COUNT } from '..'; +import { DashboardPanelState, GridData } from '../../../../common'; +import { DASHBOARD_GRID_COLUMN_COUNT } from '../../../dashboard_constants'; export type PanelPlacementMethod = ( args: PlacementArgs diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts index af4327f0fcd9..1d1aba84e7c3 100644 --- a/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts +++ b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts @@ -6,5 +6,6 @@ * Side Public License, v 1. */ -export * from './placeholder_embeddable'; -export * from './placeholder_embeddable_factory'; +export { PlaceholderEmbeddableFactory } from './placeholder_embeddable_factory'; + +export const PLACEHOLDER_EMBEDDABLE = 'placeholder'; diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx index a7fa1e793ebf..de468d86c89f 100644 --- a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx +++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx @@ -13,9 +13,9 @@ import classNames from 'classnames'; import { EuiLoadingChart } from '@elastic/eui'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { Embeddable, type EmbeddableInput, type IContainer } from '@kbn/embeddable-plugin/public'; -import { pluginServices } from '../../../services/plugin_services'; -export const PLACEHOLDER_EMBEDDABLE = 'placeholder'; +import { PLACEHOLDER_EMBEDDABLE } from '.'; +import { pluginServices } from '../../../services/plugin_services'; export class PlaceholderEmbeddable extends Embeddable { public readonly type = PLACEHOLDER_EMBEDDABLE; diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts index 74ce8bf96edb..26cdddbf17d8 100644 --- a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts +++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts @@ -13,7 +13,7 @@ import { EmbeddableInput, IContainer, } from '@kbn/embeddable-plugin/public'; -import { PlaceholderEmbeddable, PLACEHOLDER_EMBEDDABLE } from './placeholder_embeddable'; +import { PLACEHOLDER_EMBEDDABLE } from '.'; export class PlaceholderEmbeddableFactory implements EmbeddableFactoryDefinition { public readonly type = PLACEHOLDER_EMBEDDABLE; @@ -29,6 +29,7 @@ export class PlaceholderEmbeddableFactory implements EmbeddableFactoryDefinition } public async create(initialInput: EmbeddableInput, parent?: IContainer) { + const { PlaceholderEmbeddable } = await import('./placeholder_embeddable'); return new PlaceholderEmbeddable(initialInput, parent); } diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx index c462df50ef27..76a3ae7a053a 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx @@ -9,27 +9,20 @@ import React from 'react'; import { Provider } from 'react-redux'; import { createBrowserHistory } from 'history'; + +import type { Filter } from '@kbn/es-query'; +import { DataView } from '@kbn/data-views-plugin/public'; +import { EmbeddableFactory, ViewMode } from '@kbn/embeddable-plugin/public'; import { renderHook, act, RenderHookResult } from '@testing-library/react-hooks'; import { createKbnUrlStateStorage, defer } from '@kbn/kibana-utils-plugin/public'; -import { DataView } from '@kbn/data-views-plugin/public'; +import { DashboardAppState } from '../../types'; +import { getSampleDashboardInput } from '../test_helpers'; import { DashboardConstants } from '../../dashboard_constants'; -import { SavedObjectLoader } from '../../services/saved_object_loader'; -import { DashboardAppServices, DashboardAppState } from '../../types'; +import { pluginServices } from '../../services/plugin_services'; import { DashboardContainer } from '../embeddable/dashboard_container'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dashboardStateStore, setDescription, setViewMode } from '../state'; import { useDashboardAppState, UseDashboardStateProps } from './use_dashboard_app_state'; -import { - getSampleDashboardInput, - getSavedDashboardMock, - makeDefaultServices, -} from '../test_helpers'; - -import type { Filter } from '@kbn/es-query'; -import { pluginServices } from '../../services/plugin_services'; -import { EmbeddableFactory, ViewMode } from '@kbn/embeddable-plugin/public'; -import { DashboardServices } from '../../services/types'; interface SetupEmbeddableFactoryReturn { finalizeEmbeddableCreation: () => void; @@ -40,7 +33,6 @@ interface SetupEmbeddableFactoryReturn { interface RenderDashboardStateHookReturn { embeddableFactoryResult: SetupEmbeddableFactoryReturn; renderHookResult: RenderHookResult, DashboardAppState>; - services: DashboardAppServices; props: UseDashboardStateProps; } @@ -55,28 +47,7 @@ const createDashboardAppStateProps = (): UseDashboardStateProps => ({ setShowNoDataPage: () => {}, }); -const createDashboardAppStateServices = () => { - const defaults = makeDefaultServices(); - - const defaultDataView = { id: 'foo', fields: [{ name: 'bar' }] } as DataView; - - (pluginServices.getServices().data.dataViews.getDefaultDataView as jest.Mock).mockResolvedValue( - defaultDataView - ); - (pluginServices.getServices().data.dataViews.getDefaultId as jest.Mock).mockResolvedValue( - defaultDataView.id - ); - (pluginServices.getServices().data.query.filterManager.getFilters as jest.Mock).mockReturnValue( - [] - ); - - return defaults; -}; - -const setupEmbeddableFactory = ( - services: DashboardAppServices, - id: string -): SetupEmbeddableFactoryReturn => { +const setupEmbeddableFactory = (id: string): SetupEmbeddableFactoryReturn => { const dashboardContainer = new DashboardContainer({ ...getSampleDashboardInput(), id }); const deferEmbeddableCreate = defer(); pluginServices.getServices().embeddable.getEmbeddableFactory = jest.fn().mockImplementation( @@ -100,15 +71,22 @@ const setupEmbeddableFactory = ( const renderDashboardAppStateHook = ({ partialProps, - partialServices, }: { partialProps?: Partial; - partialServices?: Partial; }): RenderDashboardStateHookReturn => { + const defaultDataView = { id: 'foo', fields: [{ name: 'bar' }] } as DataView; + (pluginServices.getServices().data.dataViews.getDefaultDataView as jest.Mock).mockResolvedValue( + defaultDataView + ); + (pluginServices.getServices().data.dataViews.getDefaultId as jest.Mock).mockResolvedValue( + defaultDataView.id + ); + (pluginServices.getServices().data.query.filterManager.getFilters as jest.Mock).mockReturnValue( + [] + ); + const props = { ...createDashboardAppStateProps(), ...(partialProps ?? {}) }; - const services = { ...createDashboardAppStateServices(), ...(partialServices ?? {}) }; - const embeddableFactoryResult = setupEmbeddableFactory(services, originalDashboardEmbeddableId); - const DashboardServicesProvider = pluginServices.getContextProvider(); + const embeddableFactoryResult = setupEmbeddableFactory(originalDashboardEmbeddableId); const renderHookResult = renderHook( (replaceProps: Partial) => { @@ -116,18 +94,11 @@ const renderDashboardAppStateHook = ({ }, { wrapper: ({ children }) => { - return ( - - {/* Can't get rid of KibanaContextProvider here yet because of saved dashboard tests below */} - - {children} - - - ); + return {children}; }, } ); - return { embeddableFactoryResult, renderHookResult, services, props }; + return { embeddableFactoryResult, renderHookResult, props }; }; describe('Dashboard container lifecycle', () => { @@ -146,7 +117,7 @@ describe('Dashboard container lifecycle', () => { }); test('Old dashboard container is destroyed when new dashboardId is given', async () => { - const { renderHookResult, embeddableFactoryResult, services } = renderDashboardAppStateHook({}); + const { renderHookResult, embeddableFactoryResult } = renderDashboardAppStateHook({}); const getResult = () => renderHookResult.result.current; // on initial render dashboard container is undefined @@ -158,7 +129,7 @@ describe('Dashboard container lifecycle', () => { expect(embeddableFactoryResult.dashboardDestroySpy).not.toBeCalled(); const newDashboardId = 'wow_a_new_dashboard_id'; - const embeddableFactoryNew = setupEmbeddableFactory(services, newDashboardId); + const embeddableFactoryNew = setupEmbeddableFactory(newDashboardId); renderHookResult.rerender({ savedDashboardId: newDashboardId }); embeddableFactoryNew.finalizeEmbeddableCreation(); @@ -170,7 +141,7 @@ describe('Dashboard container lifecycle', () => { }); test('Dashboard container is destroyed if dashboard id is changed before container is resolved', async () => { - const { renderHookResult, embeddableFactoryResult, services } = renderDashboardAppStateHook({}); + const { renderHookResult, embeddableFactoryResult } = renderDashboardAppStateHook({}); const getResult = () => renderHookResult.result.current; // on initial render dashboard container is undefined @@ -178,7 +149,7 @@ describe('Dashboard container lifecycle', () => { await act(() => Promise.resolve()); // wait for the original savedDashboard to be loaded... const newDashboardId = 'wow_a_new_dashboard_id'; - const embeddableFactoryNew = setupEmbeddableFactory(services, newDashboardId); + const embeddableFactoryNew = setupEmbeddableFactory(newDashboardId); renderHookResult.rerender({ savedDashboardId: newDashboardId }); await act(() => Promise.resolve()); // wait for the new savedDashboard to be loaded... @@ -199,38 +170,33 @@ describe('Dashboard container lifecycle', () => { // FLAKY: https://github.com/elastic/kibana/issues/105018 describe.skip('Dashboard initial state', () => { it('Extracts state from Dashboard Saved Object', async () => { + const savedTitle = 'testDash1'; + ( + pluginServices.getServices().dashboardSavedObject + .loadDashboardStateFromSavedObject as jest.Mock + ).mockResolvedValue({ title: savedTitle }); + const { renderHookResult, embeddableFactoryResult } = renderDashboardAppStateHook({}); const getResult = () => renderHookResult.result.current; - // saved dashboard isn't applied until after the dashboard embeddable has been created. - expect(getResult().savedDashboard).toBeUndefined(); - embeddableFactoryResult.finalizeEmbeddableCreation(); await renderHookResult.waitForNextUpdate(); - expect(getResult().savedDashboard).toBeDefined(); - expect(getResult().savedDashboard?.title).toEqual( - getResult().getLatestDashboardState?.().title - ); + expect(savedTitle).toEqual(getResult().getLatestDashboardState?.().title); }); it('Sets initial time range and filters from saved dashboard', async () => { - const savedDashboards = {} as SavedObjectLoader; - savedDashboards.get = jest.fn().mockImplementation((id?: string) => - Promise.resolve( - getSavedDashboardMock({ - getFilters: () => [{ meta: { test: 'filterMeTimbers' } } as unknown as Filter], - timeRestore: true, - timeFrom: 'now-13d', - timeTo: 'now', - id, - }) - ) - ); - const partialServices: Partial = { savedDashboards }; - const { renderHookResult, embeddableFactoryResult, services } = renderDashboardAppStateHook({ - partialServices, + ( + pluginServices.getServices().dashboardSavedObject + .loadDashboardStateFromSavedObject as jest.Mock + ).mockResolvedValue({ + filters: [{ meta: { test: 'filterMeTimbers' } } as unknown as Filter], + timeRestore: true, + timeFrom: 'now-13d', + timeTo: 'now', }); + + const { renderHookResult, embeddableFactoryResult } = renderDashboardAppStateHook({}); const getResult = () => renderHookResult.result.current; embeddableFactoryResult.finalizeEmbeddableCreation(); @@ -238,15 +204,13 @@ describe.skip('Dashboard initial state', () => { expect(getResult().getLatestDashboardState?.().timeRestore).toEqual(true); expect( - (services as DashboardAppServices & { data: DashboardServices['data'] }).data.query.timefilter - .timefilter.setTime + pluginServices.getServices().data.query.timefilter.timefilter.setTime ).toHaveBeenCalledWith({ from: 'now-13d', to: 'now', }); expect( - (services as DashboardAppServices & { data: DashboardServices['data'] }).data.query - .filterManager.setAppFilters + pluginServices.getServices().data.query.filterManager.setAppFilters ).toHaveBeenCalledWith([{ meta: { test: 'filterMeTimbers' } } as unknown as Filter]); }); diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts index 932bfdd016b3..850c6f575904 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts @@ -6,50 +6,45 @@ * Side Public License, v 1. */ +import { omit } from 'lodash'; import { History } from 'history'; import { debounceTime, switchMap } from 'rxjs/operators'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; -import { DashboardConstants } from '../..'; -import { getNewDashboardTitle } from '../../dashboard_strings'; -import { setDashboardState, useDashboardDispatch, useDashboardSelector } from '../state'; -import type { - DashboardBuildContext, - DashboardAppServices, - DashboardAppState, - DashboardState, -} from '../../types'; -import { DashboardAppLocatorParams } from '../../locator'; import { - loadDashboardHistoryLocationState, - tryDestroyDashboardContainer, - syncDashboardContainerInput, - savedObjectToDashboardState, + diffDashboardState, + syncDashboardUrlState, syncDashboardDataViews, - syncDashboardFilterState, - loadSavedDashboardState, buildDashboardContainer, - syncDashboardUrlState, - diffDashboardState, - areTimeRangesEqual, - areRefreshIntervalsEqual, + syncDashboardFilterState, + syncDashboardContainerInput, + tryDestroyDashboardContainer, + loadDashboardHistoryLocationState, } from '../lib'; -import { isDashboardAppInNoDataState } from '../dashboard_app_no_data'; +import { + dashboardStateLoadWasSuccessful, + LoadDashboardFromSavedObjectReturn, +} from '../../services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object'; +import { DashboardConstants } from '../..'; +import { DashboardAppLocatorParams } from '../../locator'; +import { dashboardSavedObjectErrorStrings, getNewDashboardTitle } from '../../dashboard_strings'; import { pluginServices } from '../../services/plugin_services'; import { useDashboardMountContext } from './dashboard_mount_context'; +import { isDashboardAppInNoDataState } from '../dashboard_app_no_data'; +import { setDashboardState, useDashboardDispatch, useDashboardSelector } from '../state'; +import type { DashboardBuildContext, DashboardAppState, DashboardState } from '../../types'; export interface UseDashboardStateProps { history: History; showNoDataPage: boolean; savedDashboardId?: string; isEmbeddedExternally: boolean; - setShowNoDataPage: (showNoData: boolean) => void; kbnUrlStateStorage: IKbnUrlStateStorage; + setShowNoDataPage: (showNoData: boolean) => void; } export const useDashboardAppState = ({ @@ -79,25 +74,23 @@ export const useDashboardAppState = ({ const [lastSavedState, setLastSavedState] = useState(); const $onLastSavedStateChange = useMemo(() => new Subject(), []); - const { - services: { savedDashboards }, - } = useKibana(); - /** * Unpack services and context */ const { scopedHistory } = useDashboardMountContext(); const { + embeddable, + notifications: { toasts }, chrome: { docTitle }, dashboardCapabilities, dashboardSessionStorage, + spaces: { redirectLegacyUrl }, data: { query, search, dataViews }, - embeddable, initializerContext: { kibanaVersion }, screenshotMode: { isScreenshotMode, getScreenshotContext }, - spaces: { redirectLegacyUrl }, - notifications, + dashboardSavedObject: { loadDashboardStateFromSavedObject }, } = pluginServices.getServices(); + const { getStateTransfer } = embeddable; /** @@ -120,7 +113,6 @@ export const useDashboardAppState = ({ */ const dashboardBuildContext: DashboardBuildContext = { history, - savedDashboards, kbnUrlStateStorage, isEmbeddedExternally, dispatchDashboardStateChange, @@ -149,33 +141,25 @@ export const useDashboardAppState = ({ /** * Load and unpack state from dashboard saved object. */ - const loadSavedDashboardResult = await loadSavedDashboardState({ - ...dashboardBuildContext, - savedDashboardId, - }); - if (canceled || !loadSavedDashboardResult) return; - const { savedDashboard, savedDashboardState } = loadSavedDashboardResult; - - // If the saved dashboard is an alias match, then we will redirect - if (savedDashboard.outcome === 'aliasMatch' && savedDashboard.id && savedDashboard.aliasId) { - // We want to keep the "query" params on our redirect. - // But, these aren't true query params, they are technically part of the hash - // So, to get the new path, we will just replace the current id in the hash - // with the alias id - const path = scopedHistory().location.hash.replace( - savedDashboard.id, - savedDashboard.aliasId - ); - const aliasPurpose = savedDashboard.aliasPurpose; - if (isScreenshotMode()) { - scopedHistory().replace(path); - } else { - await redirectLegacyUrl?.({ path, aliasPurpose }); - } - // Return so we don't run any more of the hook and let it rerun after the redirect that just happened + let loadSavedDashboardResult: LoadDashboardFromSavedObjectReturn; + try { + loadSavedDashboardResult = await loadDashboardStateFromSavedObject({ + getScopedHistory: scopedHistory, + id: savedDashboardId, + }); + } catch (error) { + // redirect back to landing page if dashboard could not be loaded. + toasts.addDanger(dashboardSavedObjectErrorStrings.getDashboardLoadError(error.message)); + history.push(DashboardConstants.LANDING_PAGE_PATH); + return; + } + if (canceled || !dashboardStateLoadWasSuccessful(loadSavedDashboardResult)) { return; } + const { dashboardState: savedDashboardState, createConflictWarning } = + loadSavedDashboardResult; + /** * Combine initial state from the saved object, session storage, and URL, then dispatch it to Redux. */ @@ -187,12 +171,11 @@ export const useDashboardAppState = ({ const { initialDashboardStateFromUrl, stopWatchingAppStateInUrl } = syncDashboardUrlState({ ...dashboardBuildContext, - savedDashboard, }); const printLayoutDetected = isScreenshotMode() && getScreenshotContext('layout') === 'print'; - const initialDashboardState = { + const initialDashboardState: DashboardState = { ...savedDashboardState, ...dashboardSessionStorageState, ...initialDashboardStateFromUrl, @@ -208,10 +191,9 @@ export const useDashboardAppState = ({ /** * Start syncing dashboard state with the Query, Filters and Timepicker from the Query Service. */ - const { applyFilters, stopSyncingDashboardFilterState } = syncDashboardFilterState({ + const { stopSyncingDashboardFilterState } = syncDashboardFilterState({ ...dashboardBuildContext, initialDashboardState, - savedDashboard, }); /** @@ -222,10 +204,9 @@ export const useDashboardAppState = ({ ...dashboardBuildContext, initialDashboardState, incomingEmbeddable, - savedDashboard, executionContext: { type: 'dashboard', - description: savedDashboard.title, + description: initialDashboardState.title, }, }); @@ -256,15 +237,13 @@ export const useDashboardAppState = ({ const stopSyncingContainerInput = syncDashboardContainerInput({ ...dashboardBuildContext, dashboardContainer, - savedDashboard, - applyFilters, }); /** * Any time the redux state, or the last saved state changes, compare them, set the unsaved * changes state, and and push the unsaved changes to session storage. */ - const { timefilter } = query.timefilter; + const lastSavedSubscription = combineLatest([ $onLastSavedStateChange, dashboardAppState.$onDashboardStateChange, @@ -281,31 +260,24 @@ export const useDashboardAppState = ({ newState: current, }).then((unsavedChanges) => { if (observer.closed) return; - const savedTimeChanged = - lastSaved.timeRestore && - (!areTimeRangesEqual( - { - from: savedDashboard?.timeFrom, - to: savedDashboard?.timeTo, - }, - timefilter.getTime() - ) || - !areRefreshIntervalsEqual( - savedDashboard?.refreshInterval, - timefilter.getRefreshInterval() - )); - /** * changes to the dashboard should only be considered 'unsaved changes' when * editing the dashboard */ const hasUnsavedChanges = - current.viewMode === ViewMode.EDIT && - (Object.keys(unsavedChanges).length > 0 || savedTimeChanged); + current.viewMode === ViewMode.EDIT && Object.keys(unsavedChanges).length > 0; setDashboardAppState((s) => ({ ...s, hasUnsavedChanges })); unsavedChanges.viewMode = current.viewMode; // always push view mode into session store. - dashboardSessionStorage.setState(savedDashboardId, unsavedChanges); + + /** + * Current behaviour expects time range not to be backed up. + * TODO: Revisit this. It seems like we should treat all state the same. + */ + dashboardSessionStorage.setState( + savedDashboardId, + omit(unsavedChanges, ['timeRange', 'refreshInterval']) + ); }); }); }) @@ -319,11 +291,7 @@ export const useDashboardAppState = ({ setLastSavedState(savedDashboardState); dashboardBuildContext.$checkForUnsavedChanges.next(undefined); const updateLastSavedState = () => { - setLastSavedState( - savedObjectToDashboardState({ - savedDashboard, - }) - ); + setLastSavedState(dashboardBuildContext.getLatestDashboardState()); }; /** @@ -332,10 +300,9 @@ export const useDashboardAppState = ({ docTitle.change(savedDashboardState.title || getNewDashboardTitle()); setDashboardAppState((s) => ({ ...s, - applyFilters, - savedDashboard, dashboardContainer, updateLastSavedState, + createConflictWarning, getLatestDashboardState: dashboardBuildContext.getLatestDashboardState, })); @@ -359,47 +326,43 @@ export const useDashboardAppState = ({ }, [ dashboardAppState.$triggerDashboardRefresh, dashboardAppState.$onDashboardStateChange, + loadDashboardStateFromSavedObject, dispatchDashboardStateChange, $onLastSavedStateChange, dashboardSessionStorage, dashboardCapabilities, isEmbeddedExternally, + getScreenshotContext, kbnUrlStateStorage, + setShowNoDataPage, + redirectLegacyUrl, savedDashboardId, + isScreenshotMode, getStateTransfer, - savedDashboards, + showNoDataPage, scopedHistory, - notifications, - dataViews, kibanaVersion, + dataViews, embeddable, docTitle, history, + toasts, search, query, - showNoDataPage, - setShowNoDataPage, - redirectLegacyUrl, - getScreenshotContext, - isScreenshotMode, ]); /** * rebuild reset to last saved state callback whenever last saved state changes */ const resetToLastSavedState = useCallback(() => { - if ( - !lastSavedState || - !dashboardAppState.savedDashboard || - !dashboardAppState.getLatestDashboardState - ) { + if (!lastSavedState || !dashboardAppState.getLatestDashboardState) { return; } if (dashboardAppState.getLatestDashboardState().timeRestore) { const { timefilter } = query.timefilter; - const { timeFrom: from, timeTo: to, refreshInterval } = dashboardAppState.savedDashboard; - if (from && to) timefilter.setTime({ from, to }); + const { timeRange, refreshInterval } = lastSavedState; + if (timeRange) timefilter.setTime(timeRange); if (refreshInterval) timefilter.setRefreshInterval(refreshInterval); } dispatchDashboardStateChange( diff --git a/src/plugins/dashboard/public/application/lib/build_dashboard_container.ts b/src/plugins/dashboard/public/application/lib/build_dashboard_container.ts index 8ad2b7ddc52e..2d5304e002d5 100644 --- a/src/plugins/dashboard/public/application/lib/build_dashboard_container.ts +++ b/src/plugins/dashboard/public/application/lib/build_dashboard_container.ts @@ -16,8 +16,7 @@ import { isErrorEmbeddable, } from '@kbn/embeddable-plugin/public'; -import { DashboardSavedObject } from '../../saved_dashboards'; -import { DashboardContainer, DASHBOARD_CONTAINER_TYPE } from '../embeddable'; +import { DashboardContainer } from '../embeddable'; import { DashboardBuildContext, DashboardState, DashboardContainerInput } from '../../types'; import { enableDashboardSearchSessions, @@ -25,9 +24,9 @@ import { stateToDashboardContainerInput, } from '.'; import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_CONTAINER_TYPE } from '../../dashboard_constants'; type BuildDashboardContainerProps = DashboardBuildContext & { - savedDashboard: DashboardSavedObject; initialDashboardState: DashboardState; incomingEmbeddable?: EmbeddablePackageState; executionContext?: KibanaExecutionContext; @@ -41,7 +40,6 @@ export const buildDashboardContainer = async ({ initialDashboardState, isEmbeddedExternally, incomingEmbeddable, - savedDashboard, history, executionContext, }: BuildDashboardContainerProps) => { @@ -55,7 +53,6 @@ export const buildDashboardContainer = async ({ // set up search session enableDashboardSearchSessions({ - savedDashboard, initialDashboardState, getLatestDashboardState, canStoreSearchSession, @@ -95,7 +92,6 @@ export const buildDashboardContainer = async ({ dashboardState: initialDashboardState, incomingEmbeddable, searchSessionId, - savedDashboard, executionContext, }); diff --git a/src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts deleted file mode 100644 index 8e74245137f8..000000000000 --- a/src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { - convertSavedDashboardPanelToPanelState, - convertPanelStateToSavedDashboardPanel, -} from '../../../common/embeddable/embeddable_saved_object_converters'; -import { pluginServices } from '../../services/plugin_services'; -import type { SavedDashboardPanel, DashboardPanelMap } from '../../types'; - -export const convertSavedPanelsToPanelMap = (panels?: SavedDashboardPanel[]): DashboardPanelMap => { - const panelsMap: DashboardPanelMap = {}; - panels?.forEach((panel, idx) => { - panelsMap![panel.panelIndex ?? String(idx)] = convertSavedDashboardPanelToPanelState(panel); - }); - return panelsMap; -}; - -export const convertPanelMapToSavedPanels = (panels: DashboardPanelMap) => { - const { - initializerContext: { kibanaVersion }, - } = pluginServices.getServices(); - - return Object.values(panels).map((panel) => - convertPanelStateToSavedDashboardPanel(panel, kibanaVersion) - ); -}; diff --git a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts index c9e954a081ca..14e0f4ac4c17 100644 --- a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts @@ -6,37 +6,21 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import { cloneDeep, omit } from 'lodash'; import type { KibanaExecutionContext } from '@kbn/core/public'; -import type { ControlGroupInput } from '@kbn/controls-plugin/public'; -import { type EmbeddablePackageState, ViewMode } from '@kbn/embeddable-plugin/public'; -import { - compareFilters, - COMPARE_ALL_OPTIONS, - Filter, - isFilterPinned, - TimeRange, -} from '@kbn/es-query'; import { mapAndFlattenFilters } from '@kbn/data-plugin/public'; +import { type EmbeddablePackageState } from '@kbn/embeddable-plugin/public'; +import { Filter, isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS } from '@kbn/es-query'; -import type { DashboardSavedObject } from '../../saved_dashboards'; -import { getTagsFromSavedDashboard, migrateAppState } from '.'; -import { convertPanelStateToSavedDashboardPanel } from '../../../common/embeddable/embeddable_saved_object_converters'; -import type { DashboardState, RawDashboardState, DashboardContainerInput } from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; -import { deserializeControlGroupFromDashboardSavedObject } from './dashboard_control_group'; import { pluginServices } from '../../services/plugin_services'; - -interface SavedObjectToDashboardStateProps { - savedDashboard: DashboardSavedObject; -} +import { convertPanelStateToSavedDashboardPanel } from '../../../common'; +import type { DashboardState, RawDashboardState, DashboardContainerInput } from '../../types'; interface StateToDashboardContainerInputProps { searchSessionId?: string; isEmbeddedExternally?: boolean; dashboardState: DashboardState; - savedDashboard: DashboardSavedObject; incomingEmbeddable?: EmbeddablePackageState; executionContext?: KibanaExecutionContext; } @@ -44,40 +28,6 @@ interface StateToDashboardContainerInputProps { interface StateToRawDashboardStateProps { state: DashboardState; } -/** - * Converts a dashboard saved object to a dashboard state by extracting raw state from the given Dashboard - * Saved Object migrating the panel states to the latest version, then converting each panel from a saved - * dashboard panel to a panel state. - */ -export const savedObjectToDashboardState = ({ - savedDashboard, -}: SavedObjectToDashboardStateProps): DashboardState => { - const { - dashboardCapabilities: { showWriteControls }, - } = pluginServices.getServices(); - - const rawState = migrateAppState({ - fullScreenMode: false, - title: savedDashboard.title, - query: savedDashboard.getQuery(), - filters: savedDashboard.getFilters(), - timeRestore: savedDashboard.timeRestore, - description: savedDashboard.description || '', - tags: getTagsFromSavedDashboard(savedDashboard), - panels: savedDashboard.panelsJSON ? JSON.parse(savedDashboard.panelsJSON) : [], - viewMode: savedDashboard.id || showWriteControls ? ViewMode.EDIT : ViewMode.VIEW, - options: savedDashboard.optionsJSON ? JSON.parse(savedDashboard.optionsJSON) : {}, - }); - - if (rawState.timeRestore) { - rawState.timeRange = { from: savedDashboard.timeFrom, to: savedDashboard.timeTo } as TimeRange; - } - - rawState.controlGroupInput = deserializeControlGroupFromDashboardSavedObject( - savedDashboard - ) as ControlGroupInput; - return { ...rawState, panels: convertSavedPanelsToPanelMap(rawState.panels) }; -}; /** * Converts a dashboard state object to dashboard container input @@ -85,7 +35,6 @@ export const savedObjectToDashboardState = ({ export const stateToDashboardContainerInput = ({ isEmbeddedExternally, searchSessionId, - savedDashboard, dashboardState, executionContext, }: StateToDashboardContainerInputProps): DashboardContainerInput => { @@ -111,7 +60,7 @@ export const stateToDashboardContainerInput = ({ filters: dashboardFilters, } = dashboardState; - const migratedDashboardFilters = mapAndFlattenFilters(_.cloneDeep(dashboardFilters)); + const migratedDashboardFilters = mapAndFlattenFilters(cloneDeep(dashboardFilters)); return { refreshConfig: timefilter.getRefreshInterval(), filters: filterManager @@ -124,7 +73,7 @@ export const stateToDashboardContainerInput = ({ ) ), isFullScreenMode: fullScreenMode, - id: savedDashboard.id || '', + id: dashboardState.savedObjectId ?? '', isEmbeddedExternally, ...(options || {}), controlGroupInput, @@ -136,7 +85,7 @@ export const stateToDashboardContainerInput = ({ query, title, timeRange: { - ..._.cloneDeep(timefilter.getTime()), + ...cloneDeep(timefilter.getTime()), }, timeslice, timeRestore, @@ -161,5 +110,5 @@ export const stateToRawDashboardState = ({ const savedDashboardPanels = Object.values(state.panels).map((panel) => convertPanelStateToSavedDashboardPanel(panel, kibanaVersion) ); - return { ..._.omit(state, 'panels'), panels: savedDashboardPanels }; + return { ...omit(state, 'panels'), panels: savedDashboardPanels }; }; diff --git a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts index a9f474ed85dd..4f44d0cf250d 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_control_group.ts @@ -14,16 +14,15 @@ import { debounceTime, distinctUntilChanged, distinctUntilKeyChanged } from 'rxj import { ControlGroupInput, - controlGroupInputToRawControlGroupAttributes, getDefaultControlGroupInput, persistableControlGroupInputIsEqual, - rawControlGroupAttributesToControlGroupInput, + controlGroupInputToRawControlGroupAttributes, } from '@kbn/controls-plugin/common'; import { ControlGroupContainer } from '@kbn/controls-plugin/public'; import { DashboardContainer } from '..'; import { DashboardState } from '../../types'; -import { DashboardContainerInput, DashboardSavedObject } from '../..'; +import { DashboardContainerInput } from '../..'; interface DiffChecks { [key: string]: (a?: unknown, b?: unknown) => boolean; @@ -169,32 +168,17 @@ export const syncDashboardControlGroup = async ({ }; }; -export const serializeControlGroupToDashboardSavedObject = ( - dashboardSavedObject: DashboardSavedObject, - dashboardState: DashboardState +export const serializeControlGroupInput = ( + controlGroupInput: DashboardState['controlGroupInput'] ) => { // only save to saved object if control group is not default if ( - persistableControlGroupInputIsEqual( - dashboardState.controlGroupInput, - getDefaultControlGroupInput() - ) + !controlGroupInput || + persistableControlGroupInputIsEqual(controlGroupInput, getDefaultControlGroupInput()) ) { - dashboardSavedObject.controlGroupInput = undefined; - return; + return undefined; } - if (dashboardState.controlGroupInput) { - dashboardSavedObject.controlGroupInput = controlGroupInputToRawControlGroupAttributes( - dashboardState.controlGroupInput - ); - } -}; - -export const deserializeControlGroupFromDashboardSavedObject = ( - dashboardSavedObject: DashboardSavedObject -): Omit | undefined => { - if (!dashboardSavedObject.controlGroupInput) return; - return rawControlGroupAttributesToControlGroupInput(dashboardSavedObject.controlGroupInput); + return controlGroupInputToRawControlGroupAttributes(controlGroupInput); }; export const combineDashboardFiltersWithControlGroupFilters = ( diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts b/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.test.ts similarity index 89% rename from src/plugins/dashboard/public/application/lib/session_restoration.test.ts rename to src/plugins/dashboard/public/application/lib/dashboard_session_restoration.test.ts index aeb83dd8a6e4..56ee2ac55f44 100644 --- a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.test.ts @@ -6,16 +6,13 @@ * Side Public License, v 1. */ -import { getSavedDashboardMock } from '../test_helpers'; -import { createSessionRestorationDataProvider, savedObjectToDashboardState } from '.'; +import { DashboardState } from '../../types'; +import { createSessionRestorationDataProvider } from '.'; import { pluginServices } from '../../services/plugin_services'; describe('createSessionRestorationDataProvider', () => { const searchSessionInfoProvider = createSessionRestorationDataProvider({ - getAppState: () => - savedObjectToDashboardState({ - savedDashboard: getSavedDashboardMock(), - }), + getAppState: () => ({ panels: {} } as unknown as DashboardState), getDashboardTitle: () => 'Dashboard', getDashboardId: () => 'Id', }); diff --git a/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.ts b/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.ts index b2cedeee4ee0..113c39d0717b 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_session_restoration.ts @@ -17,12 +17,11 @@ import { import { getQueryParams } from '@kbn/kibana-utils-plugin/public'; import type { DashboardState } from '../../types'; -import type { DashboardSavedObject } from '../../saved_dashboards'; -import { DashboardAppLocatorParams, DashboardConstants } from '../..'; -import { getDashboardTitle } from '../../dashboard_strings'; -import { stateToRawDashboardState } from './convert_dashboard_state'; import { DASHBOARD_APP_LOCATOR } from '../../locator'; +import { getDashboardTitle } from '../../dashboard_strings'; import { pluginServices } from '../../services/plugin_services'; +import { DashboardAppLocatorParams, DashboardConstants } from '../..'; +import { stateToRawDashboardState } from './convert_dashboard_state'; export const getSearchSessionIdFromURL = (history: History): string | undefined => getQueryParams(history.location)[DashboardConstants.SEARCH_SESSION_ID] as string | undefined; @@ -52,10 +51,8 @@ export function enableDashboardSearchSessions({ canStoreSearchSession, initialDashboardState, getLatestDashboardState, - savedDashboard, }: { canStoreSearchSession: boolean; - savedDashboard: DashboardSavedObject; initialDashboardState: DashboardState; getLatestDashboardState: () => DashboardState; }) { @@ -63,13 +60,13 @@ export function enableDashboardSearchSessions({ const dashboardTitle = getDashboardTitle( initialDashboardState.title, initialDashboardState.viewMode, - !savedDashboard.id + !getLatestDashboardState().savedObjectId ); data.search.session.enableStorage( createSessionRestorationDataProvider({ getDashboardTitle: () => dashboardTitle, - getDashboardId: () => savedDashboard?.id || '', + getDashboardId: () => getLatestDashboardState().savedObjectId ?? '', getAppState: getLatestDashboardState, }), { @@ -106,7 +103,7 @@ function getLocatorParams({ return { timeRange: shouldRestoreSearchSession ? timefilter.getAbsoluteTime() : timefilter.getTime(), searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined, - panels: getDashboardId() ? undefined : appState.panels, + panels: getDashboardId() ? undefined : (appState.panels as DashboardAppLocatorParams['panels']), query: queryString.formatQuery(appState.query) as Query, filters: filterManager.getFilters(), savedQuery: appState.savedQuery, diff --git a/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts b/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts deleted file mode 100644 index 0a8ec17aeb2f..000000000000 --- a/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts +++ /dev/null @@ -1,31 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { TagDecoratedSavedObject } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import type { SavedObject } from '@kbn/saved-objects-plugin/public'; - -import { DashboardSavedObject } from '../..'; -import { pluginServices } from '../../services/plugin_services'; - -// TS is picky with type guards, we can't just inline `() => false` -function defaultTaggingGuard(_obj: SavedObject): _obj is TagDecoratedSavedObject { - return false; -} - -export const getTagsFromSavedDashboard = (savedDashboard: DashboardSavedObject) => { - const hasTaggingCapabilities = getHasTaggingCapabilitiesGuard(); - return hasTaggingCapabilities(savedDashboard) ? savedDashboard.getTags() : []; -}; - -export const getHasTaggingCapabilitiesGuard = () => { - const { - savedObjectsTagging: { hasTagDecoration }, - } = pluginServices.getServices(); - - return hasTagDecoration || defaultTaggingGuard; -}; diff --git a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts index ca913199a3ba..1c57d1bd2afa 100644 --- a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts @@ -6,14 +6,25 @@ * Side Public License, v 1. */ -import { xor, omit, isEmpty } from 'lodash'; import fastIsEqual from 'fast-deep-equal'; -import { compareFilters, COMPARE_ALL_OPTIONS, type Filter, isFilterPinned } from '@kbn/es-query'; +import { xor, omit, isEmpty, pick } from 'lodash'; + +import { + compareFilters, + COMPARE_ALL_OPTIONS, + type Filter, + isFilterPinned, + TimeRange, +} from '@kbn/es-query'; +import { RefreshInterval } from '@kbn/data-plugin/common'; import { IEmbeddable } from '@kbn/embeddable-plugin/public'; - import { persistableControlGroupInputIsEqual } from '@kbn/controls-plugin/common'; + import { DashboardContainerInput } from '../..'; -import { DashboardOptions, DashboardPanelMap, DashboardState } from '../../types'; +import { areTimesEqual } from './filter_utils'; +import { DashboardPanelMap } from '../embeddable'; +import { DashboardOptions, DashboardState } from '../../types'; +import { pluginServices } from '../../services/plugin_services'; const stateKeystoIgnore = [ 'expandedPanelId', @@ -96,15 +107,67 @@ export const diffDashboardState = async ({ newState.controlGroupInput ); + const timeStatediff = getTimeSettingsAreEqual({ + currentTimeRestore: newState.timeRestore, + lastSaved: { ...pick(originalState, ['timeRange', 'timeRestore', 'refreshInterval']) }, + }) + ? {} + : pick(newState, ['timeRange', 'timeRestore', 'refreshInterval']); + return { ...commonStateDiff, ...(panelsAreEqual ? {} : { panels: newState.panels }), ...(filtersAreEqual ? {} : { filters: newState.filters }), ...(optionsAreEqual ? {} : { options: newState.options }), ...(controlGroupIsEqual ? {} : { controlGroupInput: newState.controlGroupInput }), + ...timeStatediff, }; }; +interface TimeStateDiffArg { + timeRange?: TimeRange; + timeRestore?: boolean; + refreshInterval?: RefreshInterval; +} + +export const getTimeSettingsAreEqual = ({ + lastSaved, + currentTimeRestore, +}: { + lastSaved?: TimeStateDiffArg; + currentTimeRestore?: boolean; +}) => { + const { + data: { + query: { + timefilter: { timefilter }, + }, + }, + } = pluginServices.getServices(); + + if (currentTimeRestore !== lastSaved?.timeRestore) return false; + if (!currentTimeRestore) return true; + + const currentRange = timefilter.getTime(); + const lastRange = lastSaved?.timeRange ?? timefilter.getTimeDefaults(); + if ( + !areTimesEqual(currentRange.from, lastRange.from) || + !areTimesEqual(currentRange.to, lastRange.to) + ) { + return false; + } + + const currentInterval = timefilter.getRefreshInterval(); + const lastInterval = lastSaved?.refreshInterval ?? timefilter.getRefreshIntervalDefaults(); + if ( + currentInterval.pause !== lastInterval.pause || + currentInterval.value !== lastInterval.value + ) { + return false; + } + return true; +}; + const getFiltersAreEqual = ( filtersA: Filter[], filtersB: Filter[], diff --git a/src/plugins/dashboard/public/application/lib/filter_utils.ts b/src/plugins/dashboard/public/application/lib/filter_utils.ts index 9b9a1270fd3b..fb2762c7dc58 100644 --- a/src/plugins/dashboard/public/application/lib/filter_utils.ts +++ b/src/plugins/dashboard/public/application/lib/filter_utils.ts @@ -8,12 +8,7 @@ import _ from 'lodash'; import moment, { Moment } from 'moment'; -import type { Optional } from '@kbn/utility-types'; -import type { RefreshInterval } from '@kbn/data-plugin/public'; -import type { Filter, TimeRange } from '@kbn/es-query'; - -type TimeRangeCompare = Optional; -type RefreshIntervalCompare = Optional; +import type { Filter } from '@kbn/es-query'; /** * Converts the time to a utc formatted string. If the time is not valid (e.g. it might be in a relative format like @@ -32,22 +27,6 @@ export const convertTimeToUTCString = (time?: string | Moment): undefined | stri } }; -export const areTimeRangesEqual = (rangeA: TimeRangeCompare, rangeB: TimeRangeCompare): boolean => - areTimesEqual(rangeA.from, rangeB.from) && areTimesEqual(rangeA.to, rangeB.to); - -export const areRefreshIntervalsEqual = ( - refreshA?: RefreshIntervalCompare, - refreshB?: RefreshIntervalCompare -): boolean => refreshA?.pause === refreshB?.pause && refreshA?.value === refreshB?.value; - -/** - * Compares the two times, making sure they are in both compared in string format. Absolute times - * are sometimes stored as moment objects, but converted to strings when reloaded. Relative times are - * strings that are not convertible to moment objects. - * @param timeA {string|Moment} - * @param timeB {string|Moment} - * @returns {boolean} - */ export const areTimesEqual = (timeA?: string | Moment, timeB?: string | Moment) => { return convertTimeToUTCString(timeA) === convertTimeToUTCString(timeB); }; diff --git a/src/plugins/dashboard/public/application/lib/index.ts b/src/plugins/dashboard/public/application/lib/index.ts index 1b4ab12d2bc1..0f364a31061d 100644 --- a/src/plugins/dashboard/public/application/lib/index.ts +++ b/src/plugins/dashboard/public/application/lib/index.ts @@ -6,28 +6,24 @@ * Side Public License, v 1. */ -export * from './filter_utils'; -export { getDashboardIdFromUrl } from './url'; -export { saveDashboard } from './save_dashboard'; -export { migrateAppState } from './migrate_app_state'; -export { addHelpMenuToAppChrome } from './help_menu_util'; -export { diffDashboardState } from './diff_dashboard_state'; -export { getTagsFromSavedDashboard } from './dashboard_tagging'; -export { syncDashboardUrlState } from './sync_dashboard_url_state'; -export { loadSavedDashboardState } from './load_saved_dashboard_state'; -export { attemptLoadDashboardByTitle } from './load_dashboard_by_title'; -export { syncDashboardFilterState } from './sync_dashboard_filter_state'; -export { syncDashboardDataViews } from './sync_dashboard_data_views'; -export { syncDashboardContainerInput } from './sync_dashboard_container_input'; -export { loadDashboardHistoryLocationState } from './load_dashboard_history_location_state'; -export { buildDashboardContainer, tryDestroyDashboardContainer } from './build_dashboard_container'; export { - stateToDashboardContainerInput, - savedObjectToDashboardState, -} from './convert_dashboard_state'; + areTimesEqual, + convertTimeToUTCString, + cleanFiltersForSerialize, + cleanFiltersForComparison, +} from './filter_utils'; export { createSessionRestorationDataProvider, enableDashboardSearchSessions, getSearchSessionIdFromURL, getSessionURLObservable, } from './dashboard_session_restoration'; +export { addHelpMenuToAppChrome } from './help_menu_util'; +export { diffDashboardState } from './diff_dashboard_state'; +export { syncDashboardUrlState } from './sync_dashboard_url_state'; +export { syncDashboardDataViews } from './sync_dashboard_data_views'; +export { syncDashboardFilterState } from './sync_dashboard_filter_state'; +export { stateToDashboardContainerInput } from './convert_dashboard_state'; +export { syncDashboardContainerInput } from './sync_dashboard_container_input'; +export { loadDashboardHistoryLocationState } from './load_dashboard_history_location_state'; +export { buildDashboardContainer, tryDestroyDashboardContainer } from './build_dashboard_container'; diff --git a/src/plugins/dashboard/public/application/lib/load_dashboard_by_title.ts b/src/plugins/dashboard/public/application/lib/load_dashboard_by_title.ts deleted file mode 100644 index bff9f8600c0e..000000000000 --- a/src/plugins/dashboard/public/application/lib/load_dashboard_by_title.ts +++ /dev/null @@ -1,31 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { DashboardSavedObject } from '../..'; -import { pluginServices } from '../../services/plugin_services'; - -export async function attemptLoadDashboardByTitle( - title: string -): Promise<{ id: string } | undefined> { - const { - savedObjects: { client }, - } = pluginServices.getServices(); - - const results = await client.find({ - search: `"${title}"`, - searchFields: ['title'], - type: 'dashboard', - }); - // The search isn't an exact match, lets see if we can find a single exact match to use - const matchingDashboards = results.savedObjects.filter( - (dashboard) => dashboard.attributes.title.toLowerCase() === title.toLowerCase() - ); - if (matchingDashboards.length === 1) { - return { id: matchingDashboards[0].id }; - } -} diff --git a/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts b/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts index ce06ef443d69..9a7d1791c6c9 100644 --- a/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts +++ b/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { ForwardedDashboardState } from '../../locator'; import { DashboardState } from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; +import { ForwardedDashboardState } from '../../locator'; +import { convertSavedPanelsToPanelMap } from '../../../common'; export const loadDashboardHistoryLocationState = ( state?: ForwardedDashboardState diff --git a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts deleted file mode 100644 index 6a7eba0884ab..000000000000 --- a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts +++ /dev/null @@ -1,74 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { getDashboard60Warning, dashboardLoadingErrorStrings } from '../../dashboard_strings'; -import { savedObjectToDashboardState } from './convert_dashboard_state'; -import { DashboardState, DashboardBuildContext } from '../../types'; -import { DashboardConstants, DashboardSavedObject } from '../..'; -import { migrateLegacyQuery } from './migrate_legacy_query'; -import { cleanFiltersForSerialize } from './filter_utils'; -import { pluginServices } from '../../services/plugin_services'; - -interface LoadSavedDashboardStateReturn { - savedDashboardState: DashboardState; - savedDashboard: DashboardSavedObject; -} - -/** - * Loads, migrates, and returns state from a dashboard saved object. - */ -export const loadSavedDashboardState = async ({ - history, - savedDashboards, - savedDashboardId, -}: DashboardBuildContext & { savedDashboardId?: string }): Promise< - LoadSavedDashboardStateReturn | undefined -> => { - const { - dashboardCapabilities: { showWriteControls }, - data: { - query: { queryString }, - }, - notifications: { toasts }, - } = pluginServices.getServices(); - - // BWC - remove for 8.0 - if (savedDashboardId === 'create') { - history.replace({ - ...history.location, // preserve query, - pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL, - }); - - toasts.addWarning(getDashboard60Warning()); - return; - } - try { - const savedDashboard = (await savedDashboards.get({ - id: savedDashboardId, - useResolve: true, - })) as DashboardSavedObject; - const savedDashboardState = savedObjectToDashboardState({ - savedDashboard, - }); - - const isViewMode = !showWriteControls || Boolean(savedDashboard.id); - savedDashboardState.viewMode = isViewMode ? ViewMode.VIEW : ViewMode.EDIT; - savedDashboardState.filters = cleanFiltersForSerialize(savedDashboardState.filters); - savedDashboardState.query = migrateLegacyQuery( - savedDashboardState.query || queryString.getDefaultQuery() - ); - - return { savedDashboardState, savedDashboard }; - } catch (error) { - // E.g. a corrupt or deleted dashboard - toasts.addDanger(dashboardLoadingErrorStrings.getDashboardLoadError(error.message)); - history.push(DashboardConstants.LANDING_PAGE_PATH); - return; - } -}; diff --git a/src/plugins/dashboard/public/application/lib/migrate_app_state.test.ts b/src/plugins/dashboard/public/application/lib/migrate_app_state.test.ts deleted file mode 100644 index 578439070e97..000000000000 --- a/src/plugins/dashboard/public/application/lib/migrate_app_state.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { pluginServices } from '../../services/plugin_services'; -import { SavedDashboardPanel } from '../../types'; -import { migrateAppState } from './migrate_app_state'; - -pluginServices.getServices().initializerContext.kibanaVersion = '8.0'; - -test('migrate app state from 6.0', async () => { - const appState = { - uiState: { - 'P-1': { vis: { defaultColors: { '0+-+100': 'rgb(0,104,55)' } } }, - }, - panels: [ - { - col: 1, - id: 'Visualization-MetricChart', - panelIndex: 1, - row: 1, - size_x: 6, - size_y: 3, - type: 'visualization', - }, - ], - }; - migrateAppState(appState as any); - expect(appState.uiState).toBeUndefined(); - - const newPanel = appState.panels[0] as unknown as SavedDashboardPanel; - - expect(newPanel.gridData.w).toBe(24); - expect(newPanel.gridData.h).toBe(15); - expect(newPanel.gridData.x).toBe(0); - expect(newPanel.gridData.y).toBe(0); - - expect((newPanel.embeddableConfig as any).vis.defaultColors['0+-+100']).toBe('rgb(0,104,55)'); -}); - -test('migrate sort from 6.1', async () => { - const appState = { - uiState: { - 'P-1': { vis: { defaultColors: { '0+-+100': 'rgb(0,104,55)' } } }, - }, - panels: [ - { - col: 1, - id: 'Visualization-MetricChart', - panelIndex: 1, - row: 1, - size_x: 6, - size_y: 3, - type: 'visualization', - sort: 'sort', - }, - ], - useMargins: false, - }; - migrateAppState(appState as any); - expect(appState.uiState).toBeUndefined(); - - const newPanel = appState.panels[0] as unknown as SavedDashboardPanel; - expect(newPanel.gridData.w).toBe(24); - expect(newPanel.gridData.h).toBe(15); - expect((newPanel as any).sort).toBeUndefined(); - - expect((newPanel.embeddableConfig as any).sort).toBe('sort'); - expect((newPanel.embeddableConfig as any).vis.defaultColors['0+-+100']).toBe('rgb(0,104,55)'); -}); - -test('migrates 6.0 even when uiState does not exist', async () => { - const appState = { - panels: [ - { - col: 1, - id: 'Visualization-MetricChart', - panelIndex: 1, - row: 1, - size_x: 6, - size_y: 3, - type: 'visualization', - sort: 'sort', - }, - ], - }; - migrateAppState(appState as any); - expect((appState as any).uiState).toBeUndefined(); - - const newPanel = appState.panels[0] as unknown as SavedDashboardPanel; - expect(newPanel.gridData.w).toBe(24); - expect(newPanel.gridData.h).toBe(15); - expect((newPanel as any).sort).toBeUndefined(); - - expect((newPanel.embeddableConfig as any).sort).toBe('sort'); -}); - -test('6.2 migration adjusts w & h without margins', async () => { - const appState = { - panels: [ - { - id: 'Visualization-MetricChart', - panelIndex: 1, - gridData: { - h: 3, - w: 7, - x: 2, - y: 5, - }, - type: 'visualization', - sort: 'sort', - version: '6.2.0', - }, - ], - useMargins: false, - }; - migrateAppState(appState as any); - expect((appState as any).uiState).toBeUndefined(); - - const newPanel = appState.panels[0] as unknown as SavedDashboardPanel; - expect(newPanel.gridData.w).toBe(28); - expect(newPanel.gridData.h).toBe(15); - expect(newPanel.gridData.x).toBe(8); - expect(newPanel.gridData.y).toBe(25); - expect((newPanel as any).sort).toBeUndefined(); - - expect((newPanel.embeddableConfig as any).sort).toBe('sort'); -}); - -test('6.2 migration adjusts w & h with margins', async () => { - const appState = { - panels: [ - { - id: 'Visualization-MetricChart', - panelIndex: 1, - gridData: { - h: 3, - w: 7, - x: 2, - y: 5, - }, - type: 'visualization', - sort: 'sort', - version: '6.2.0', - }, - ], - useMargins: true, - }; - migrateAppState(appState as any); - expect((appState as any).uiState).toBeUndefined(); - - const newPanel = appState.panels[0] as unknown as SavedDashboardPanel; - expect(newPanel.gridData.w).toBe(28); - expect(newPanel.gridData.h).toBe(12); - expect(newPanel.gridData.x).toBe(8); - expect(newPanel.gridData.y).toBe(20); - expect((newPanel as any).sort).toBeUndefined(); - - expect((newPanel.embeddableConfig as any).sort).toBe('sort'); -}); diff --git a/src/plugins/dashboard/public/application/lib/migrate_app_state.ts b/src/plugins/dashboard/public/application/lib/migrate_app_state.ts deleted file mode 100644 index e077aab89ea8..000000000000 --- a/src/plugins/dashboard/public/application/lib/migrate_app_state.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import semverSatisfies from 'semver/functions/satisfies'; - -import { i18n } from '@kbn/i18n'; -import { METRIC_TYPE } from '@kbn/analytics'; -import type { SerializableRecord } from '@kbn/utility-types'; - -import { RawDashboardState, SavedDashboardPanel } from '../../types'; -import type { - SavedDashboardPanelTo60, - SavedDashboardPanel730ToLatest, - SavedDashboardPanel610, - SavedDashboardPanel630, - SavedDashboardPanel640To720, - SavedDashboardPanel620, -} from '../../../common'; -import { migratePanelsTo730 } from '../../../common'; -import { pluginServices } from '../../services/plugin_services'; - -/** - * Attempts to migrate the state stored in the URL into the latest version of it. - * - * Once we hit a major version, we can remove support for older style URLs and get rid of this logic. - */ -export function migrateAppState( - appState: { [key: string]: any } & RawDashboardState -): RawDashboardState { - if (!appState.panels) { - throw new Error( - i18n.translate('dashboard.panel.invalidData', { - defaultMessage: 'Invalid data in url', - }) - ); - } - - const { - usageCollection: { reportUiCounter }, - initializerContext: { kibanaVersion }, - } = pluginServices.getServices(); - - const panelNeedsMigration = ( - appState.panels as Array< - | SavedDashboardPanelTo60 - | SavedDashboardPanel610 - | SavedDashboardPanel620 - | SavedDashboardPanel630 - | SavedDashboardPanel640To720 - | SavedDashboardPanel730ToLatest - > - ).some((panel) => { - if ((panel as { version?: string }).version === undefined) return true; - - const version = (panel as SavedDashboardPanel730ToLatest).version; - - if (reportUiCounter) { - // This will help us figure out when to remove support for older style URLs. - reportUiCounter('DashboardPanelVersionInUrl', METRIC_TYPE.LOADED, `${version}`); - } - - return semverSatisfies(version, '<7.3'); - }); - - if (panelNeedsMigration) { - appState.panels = migratePanelsTo730( - appState.panels as Array< - | SavedDashboardPanelTo60 - | SavedDashboardPanel610 - | SavedDashboardPanel620 - | SavedDashboardPanel630 - | SavedDashboardPanel640To720 - >, - kibanaVersion, - appState.useMargins as boolean, - appState.uiState as { [key: string]: SerializableRecord } - ) as SavedDashboardPanel[]; - delete appState.uiState; - } - - return appState; -} diff --git a/src/plugins/dashboard/public/application/lib/save_dashboard.ts b/src/plugins/dashboard/public/application/lib/save_dashboard.ts deleted file mode 100644 index f3347ca3f204..000000000000 --- a/src/plugins/dashboard/public/application/lib/save_dashboard.ts +++ /dev/null @@ -1,120 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import _ from 'lodash'; - -import { isFilterPinned } from '@kbn/es-query'; -import type { RefreshInterval } from '@kbn/data-plugin/public'; -import type { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; - -import { convertTimeToUTCString } from '.'; -import type { DashboardSavedObject } from '../../saved_dashboards'; -import { dashboardSaveToastStrings } from '../../dashboard_strings'; -import { getHasTaggingCapabilitiesGuard } from './dashboard_tagging'; -import type { DashboardRedirect, DashboardState } from '../../types'; -import { serializeControlGroupToDashboardSavedObject } from './dashboard_control_group'; -import { convertPanelStateToSavedDashboardPanel } from '../../../common/embeddable/embeddable_saved_object_converters'; -import { pluginServices } from '../../services/plugin_services'; - -export type SavedDashboardSaveOpts = SavedObjectSaveOpts & { stayInEditMode?: boolean }; - -interface SaveDashboardProps { - redirectTo: DashboardRedirect; - currentState: DashboardState; - saveOptions: SavedDashboardSaveOpts; - savedDashboard: DashboardSavedObject; -} - -export const saveDashboard = async ({ - redirectTo, - saveOptions, - currentState, - savedDashboard, -}: SaveDashboardProps): Promise<{ id?: string; redirected?: boolean; error?: any }> => { - const { - data: { - query: { - timefilter: { timefilter }, - }, - }, - dashboardSessionStorage, - initializerContext: { kibanaVersion }, - notifications, - } = pluginServices.getServices(); - - const lastDashboardId = savedDashboard.id; - const hasTaggingCapabilities = getHasTaggingCapabilitiesGuard(); - - const { panels, title, tags, description, timeRestore, options } = currentState; - - const savedDashboardPanels = Object.values(panels).map((panel) => - convertPanelStateToSavedDashboardPanel(panel, kibanaVersion) - ); - - savedDashboard.title = title; - savedDashboard.description = description; - savedDashboard.timeRestore = timeRestore; - savedDashboard.optionsJSON = JSON.stringify(options); - savedDashboard.panelsJSON = JSON.stringify(savedDashboardPanels); - - // control group input - serializeControlGroupToDashboardSavedObject(savedDashboard, currentState); - - if (hasTaggingCapabilities(savedDashboard)) { - savedDashboard.setTags(tags); - } - - const { from, to } = timefilter.getTime(); - savedDashboard.timeFrom = savedDashboard.timeRestore ? convertTimeToUTCString(from) : undefined; - savedDashboard.timeTo = savedDashboard.timeRestore ? convertTimeToUTCString(to) : undefined; - - const timeRestoreObj: RefreshInterval = _.pick(timefilter.getRefreshInterval(), [ - 'display', - 'pause', - 'section', - 'value', - ]) as RefreshInterval; - savedDashboard.refreshInterval = savedDashboard.timeRestore ? timeRestoreObj : undefined; - - // only save unpinned filters - const unpinnedFilters = savedDashboard.getFilters().filter((filter) => !isFilterPinned(filter)); - savedDashboard.searchSource.setField('filter', unpinnedFilters); - - try { - const newId = await savedDashboard.save(saveOptions); - if (newId) { - notifications.toasts.addSuccess({ - title: dashboardSaveToastStrings.getSuccessString(currentState.title), - 'data-test-subj': 'saveDashboardSuccess', - }); - - /** - * If the dashboard id has been changed, redirect to the new ID to keep the url param in sync. - */ - if (newId !== lastDashboardId) { - dashboardSessionStorage.clearState(lastDashboardId); - redirectTo({ - id: newId, - editMode: true, - useReplace: true, - destination: 'dashboard', - }); - return { redirected: true, id: newId }; - } - } - return { id: newId }; - } catch (error) { - notifications.toasts.addDanger( - dashboardSaveToastStrings.getFailureString(currentState.title, error.message), - { - 'data-test-subj': 'saveDashboardFailure', - } - ); - return { error }; - } -}; diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts index 67e098186f9e..9e97beaad276 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts @@ -10,12 +10,11 @@ import _ from 'lodash'; import { Subscription } from 'rxjs'; import { debounceTime, tap } from 'rxjs/operators'; -import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; +import { compareFilters, COMPARE_ALL_OPTIONS } from '@kbn/es-query'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/public'; -import type { Query } from '@kbn/es-query'; import type { DashboardContainer } from '../embeddable'; -import { DashboardConstants, type DashboardSavedObject } from '../..'; +import { DashboardConstants } from '../..'; import { setControlGroupState, setExpandedPanelId, @@ -36,16 +35,13 @@ import { pluginServices } from '../../services/plugin_services'; type SyncDashboardContainerCommon = DashboardBuildContext & { dashboardContainer: DashboardContainer; - savedDashboard: DashboardSavedObject; }; type ApplyStateChangesToContainerProps = SyncDashboardContainerCommon & { force: boolean; }; -type ApplyContainerChangesToStateProps = SyncDashboardContainerCommon & { - applyFilters: (query: Query, filters: Filter[]) => void; -}; +type ApplyContainerChangesToStateProps = SyncDashboardContainerCommon; type SyncDashboardContainerProps = SyncDashboardContainerCommon & ApplyContainerChangesToStateProps; @@ -94,7 +90,6 @@ export const syncDashboardContainerInput = ( }; export const applyContainerChangesToState = ({ - applyFilters, dashboardContainer, getLatestDashboardState, dispatchDashboardStateChange, @@ -112,7 +107,6 @@ export const applyContainerChangesToState = ({ if (!compareFilters(input.filters, filterManager.getFilters(), COMPARE_ALL_OPTIONS)) { // Add filters modifies the object passed to it, hence the clone deep. filterManager.addFilters(_.cloneDeep(input.filters)); - applyFilters(latestState.query, input.filters); } if (!_.isEqual(input.panels, latestState.panels)) { @@ -144,7 +138,6 @@ export const applyContainerChangesToState = ({ export const applyStateChangesToContainer = ({ force, history, - savedDashboard, dashboardContainer, kbnUrlStateStorage, isEmbeddedExternally, @@ -161,7 +154,6 @@ export const applyStateChangesToContainer = ({ const currentDashboardStateAsInput = stateToDashboardContainerInput({ dashboardState: latestState, isEmbeddedExternally, - savedDashboard, }); const differences = diffDashboardContainerInput( dashboardContainer.getInput(), diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts index d02e674936ae..0bce899e22fc 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts @@ -8,25 +8,22 @@ import _ from 'lodash'; import { merge } from 'rxjs'; -import { debounceTime, finalize, map, switchMap, tap } from 'rxjs/operators'; +import { finalize, map, switchMap, tap } from 'rxjs/operators'; import { connectToQueryState, GlobalQueryStateFromUrl, - syncQueryStateWithUrl, + syncGlobalQueryStateWithUrl, waitUntilNextSessionCompletes$, } from '@kbn/data-plugin/public'; import type { Filter, Query } from '@kbn/es-query'; import { cleanFiltersForSerialize } from '.'; -import { setQuery } from '../state'; import type { DashboardBuildContext, DashboardState } from '../../types'; -import type { DashboardSavedObject } from '../../saved_dashboards'; import { setFiltersAndQuery } from '../state/dashboard_state_slice'; import { pluginServices } from '../../services/plugin_services'; type SyncDashboardFilterStateProps = DashboardBuildContext & { initialDashboardState: DashboardState; - savedDashboard: DashboardSavedObject; }; /** @@ -35,7 +32,6 @@ type SyncDashboardFilterStateProps = DashboardBuildContext & { * and the dashboard Redux store. */ export const syncDashboardFilterState = ({ - savedDashboard, kbnUrlStateStorage, initialDashboardState, $checkForUnsavedChanges, @@ -46,25 +42,17 @@ export const syncDashboardFilterState = ({ const { data: { query: queryService, search }, } = pluginServices.getServices(); - const { filterManager, queryString, timefilter } = queryService; + const { queryString, timefilter } = queryService; const { timefilter: timefilterService } = timefilter; // apply initial dashboard filter state. applyDashboardFilterState({ currentDashboardState: initialDashboardState, kbnUrlStateStorage, - savedDashboard, }); - // this callback will be used any time new filters and query need to be applied. - const applyFilters = (query: Query, filters: Filter[]) => { - savedDashboard.searchSource.setField('query', query); - savedDashboard.searchSource.setField('filter', filters); - dispatchDashboardStateChange(setQuery(query)); - }; - // starts syncing `_g` portion of url with query services - const { stop: stopSyncingQueryServiceStateWithUrl } = syncQueryStateWithUrl( + const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( queryService, kbnUrlStateStorage ); @@ -81,7 +69,6 @@ export const syncDashboardFilterState = ({ set: ({ filters, query }) => { intermediateFilterState.filters = cleanFiltersForSerialize(filters ?? []) || []; intermediateFilterState.query = query || queryString.getDefaultQuery(); - applyFilters(intermediateFilterState.query, intermediateFilterState.filters); dispatchDashboardStateChange(setFiltersAndQuery(intermediateFilterState)); }, state$: $onDashboardStateChange.pipe( @@ -97,11 +84,6 @@ export const syncDashboardFilterState = ({ } ); - // apply filters when the filter manager changes - const filterManagerSubscription = merge(filterManager.getUpdates$(), queryString.getUpdates$()) - .pipe(debounceTime(100)) - .subscribe(() => applyFilters(queryString.getQuery() as Query, filterManager.getFilters())); - const timeRefreshSubscription = merge( timefilterService.getRefreshIntervalUpdate$(), timefilterService.getTimeUpdate$() @@ -127,26 +109,23 @@ export const syncDashboardFilterState = ({ .subscribe(); const stopSyncingDashboardFilterState = () => { - filterManagerSubscription.unsubscribe(); forceRefreshSubscription.unsubscribe(); timeRefreshSubscription.unsubscribe(); stopSyncingQueryServiceStateWithUrl(); stopSyncingAppFilters(); }; - return { applyFilters, stopSyncingDashboardFilterState }; + return { stopSyncingDashboardFilterState }; }; interface ApplyDashboardFilterStateProps { kbnUrlStateStorage: DashboardBuildContext['kbnUrlStateStorage']; currentDashboardState: DashboardState; - savedDashboard: DashboardSavedObject; } export const applyDashboardFilterState = ({ currentDashboardState, kbnUrlStateStorage, - savedDashboard, }: ApplyDashboardFilterStateProps) => { const { data: { @@ -155,13 +134,9 @@ export const applyDashboardFilterState = ({ } = pluginServices.getServices(); const { timefilter: timefilterService } = timefilter; - // apply filters to the query service and to the saved dashboard + // apply filters and query to the query service filterManager.setAppFilters(_.cloneDeep(currentDashboardState.filters)); - savedDashboard.searchSource.setField('filter', currentDashboardState.filters); - - // apply query to the query service and to the saved dashboard queryString.setQuery(currentDashboardState.query); - savedDashboard.searchSource.setField('query', currentDashboardState.query); /** * If a global time range is not set explicitly and the time range was saved with the dashboard, apply @@ -169,18 +144,11 @@ export const applyDashboardFilterState = ({ */ if (currentDashboardState.timeRestore) { const globalQueryState = kbnUrlStateStorage.get('_g'); - if (!globalQueryState?.time) { - if (savedDashboard.timeFrom && savedDashboard.timeTo) { - timefilterService.setTime({ - from: savedDashboard.timeFrom, - to: savedDashboard.timeTo, - }); - } + if (!globalQueryState?.time && currentDashboardState.timeRange) { + timefilterService.setTime(currentDashboardState.timeRange); } - if (!globalQueryState?.refreshInterval) { - if (savedDashboard.refreshInterval) { - timefilterService.setRefreshInterval(savedDashboard.refreshInterval); - } + if (!globalQueryState?.refreshInterval && currentDashboardState.refreshInterval) { + timefilterService.setRefreshInterval(currentDashboardState.refreshInterval); } } }; diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts index 947e3f5d69de..31101ae3679f 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts @@ -8,41 +8,52 @@ import _ from 'lodash'; import { debounceTime } from 'rxjs/operators'; +import semverSatisfies from 'semver/functions/satisfies'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/public'; -import { migrateAppState } from '.'; -import { DashboardSavedObject } from '../..'; + import { setDashboardState } from '../state'; import { migrateLegacyQuery } from './migrate_legacy_query'; -import { applyDashboardFilterState } from './sync_dashboard_filter_state'; +import { pluginServices } from '../../services/plugin_services'; import { DASHBOARD_STATE_STORAGE_KEY } from '../../dashboard_constants'; -import type { - DashboardBuildContext, - DashboardPanelMap, - DashboardState, - RawDashboardState, -} from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; +import { applyDashboardFilterState } from './sync_dashboard_filter_state'; +import { dashboardSavedObjectErrorStrings } from '../../dashboard_strings'; +import { convertSavedPanelsToPanelMap, DashboardPanelMap } from '../../../common'; +import type { DashboardBuildContext, DashboardState, RawDashboardState } from '../../types'; -type SyncDashboardUrlStateProps = DashboardBuildContext & { savedDashboard: DashboardSavedObject }; +/** + * We no longer support loading panels from a version older than 7.3 in the URL. + * @returns whether or not there is a panel in the URL state saved with a version before 7.3 + */ +export const isPanelVersionTooOld = (panels: RawDashboardState['panels']) => { + for (const panel of panels) { + if (!panel.version || semverSatisfies(panel.version, '<7.3')) return true; + } + return false; +}; export const syncDashboardUrlState = ({ dispatchDashboardStateChange, getLatestDashboardState, kbnUrlStateStorage, - savedDashboard, -}: SyncDashboardUrlStateProps) => { +}: DashboardBuildContext) => { /** * Loads any dashboard state from the URL, and removes the state from the URL. */ const loadAndRemoveDashboardState = (): Partial => { + const { + notifications: { toasts }, + } = pluginServices.getServices(); const rawAppStateInUrl = kbnUrlStateStorage.get(DASHBOARD_STATE_STORAGE_KEY); if (!rawAppStateInUrl) return {}; - let panelsMap: DashboardPanelMap = {}; + let panelsMap: DashboardPanelMap | undefined; if (rawAppStateInUrl.panels && rawAppStateInUrl.panels.length > 0) { - const rawState = migrateAppState(rawAppStateInUrl); - panelsMap = convertSavedPanelsToPanelMap(rawState.panels); + if (isPanelVersionTooOld(rawAppStateInUrl.panels)) { + toasts.addWarning(dashboardSavedObjectErrorStrings.getPanelTooOldError()); + } else { + panelsMap = convertSavedPanelsToPanelMap(rawAppStateInUrl.panels); + } } const migratedQuery = rawAppStateInUrl.query @@ -58,7 +69,7 @@ export const syncDashboardUrlState = ({ return { ..._.omit(rawAppStateInUrl, ['panels', 'query']), ...(migratedQuery ? { query: migratedQuery } : {}), - ...(rawAppStateInUrl.panels ? { panels: panelsMap } : {}), + ...(panelsMap ? { panels: panelsMap } : {}), }; }; @@ -75,7 +86,6 @@ export const syncDashboardUrlState = ({ applyDashboardFilterState({ currentDashboardState: updatedDashboardState, kbnUrlStateStorage, - savedDashboard, }); if (Object.keys(stateFromUrl).length === 0) return; diff --git a/src/plugins/dashboard/public/application/lib/url.test.ts b/src/plugins/dashboard/public/application/lib/url.test.ts deleted file mode 100644 index fc7e51b8c2e3..000000000000 --- a/src/plugins/dashboard/public/application/lib/url.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { getDashboardIdFromUrl } from './url'; - -test('getDashboardIdFromUrl', () => { - let url = - "http://localhost:5601/wev/app/dashboards#/create?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getDashboardIdFromUrl(url)).toEqual(undefined); - - url = - "http://localhost:5601/wev/app/dashboards#/view/625357282?_a=(description:'',filters:!()&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"; - expect(getDashboardIdFromUrl(url)).toEqual('625357282'); - - url = 'http://myserver.mydomain.com:5601/wev/app/dashboards#/view/777182'; - expect(getDashboardIdFromUrl(url)).toEqual('777182'); - - url = - "http://localhost:5601/app/dashboards#/create?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!()"; - expect(getDashboardIdFromUrl(url)).toEqual(undefined); - - url = '/view/test/?_g=(refreshInterval:'; - expect(getDashboardIdFromUrl(url)).toEqual('test'); - - url = 'view/test/?_g=(refreshInterval:'; - expect(getDashboardIdFromUrl(url)).toEqual('test'); - - url = '/other-app/test/'; - expect(getDashboardIdFromUrl(url)).toEqual(undefined); -}); diff --git a/src/plugins/dashboard/public/application/lib/url.ts b/src/plugins/dashboard/public/application/lib/url.ts deleted file mode 100644 index 0ff2809a9266..000000000000 --- a/src/plugins/dashboard/public/application/lib/url.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** - * Returns dashboard id from URL - * literally looks from id after `dashboard/` string and before `/`, `?` and end of string - * @param url to extract dashboardId from - * input: http://localhost:5601/lib/app/kibana#/dashboard?param1=x¶m2=y¶m3=z - * output: undefined - * input: http://localhost:5601/lib/app/kibana#/dashboard/39292992?param1=x¶m2=y¶m3=z - * output: 39292992 - */ -export function getDashboardIdFromUrl(url: string): string | undefined { - const [, dashboardId] = url.match(/view\/(.*?)(\/|\?|$)/) ?? [ - undefined, // full match - undefined, // group with dashboardId - ]; - return dashboardId ?? undefined; -} diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx index 5712b4c6ee2f..4a77249aeb39 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx @@ -9,19 +9,15 @@ import React from 'react'; import { mount } from 'enzyme'; -import { I18nProvider, FormattedRelative } from '@kbn/i18n-react'; -import { SimpleSavedObject } from '@kbn/core/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { TableListViewKibanaDependencies, TableListViewKibanaProvider, } from '@kbn/content-management-table-list'; +import { I18nProvider, FormattedRelative } from '@kbn/i18n-react'; +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; -import { DashboardAppServices } from '../../types'; -import { DashboardListing, DashboardListingProps } from './dashboard_listing'; -import { makeDefaultServices } from '../test_helpers'; import { pluginServices } from '../../services/plugin_services'; +import { DashboardListing, DashboardListingProps } from './dashboard_listing'; import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; function makeDefaultProps(): DashboardListingProps { @@ -31,14 +27,7 @@ function makeDefaultProps(): DashboardListingProps { }; } -function mountWith({ - props: incomingProps, - services: incomingServices, -}: { - props?: DashboardListingProps; - services?: DashboardAppServices; -}) { - const services = incomingServices ?? makeDefaultServices(); +function mountWith({ props: incomingProps }: { props?: DashboardListingProps }) { const props = incomingProps ?? makeDefaultProps(); const wrappingComponent: React.FC<{ children: React.ReactNode; @@ -47,35 +36,32 @@ function mountWith({ return ( - {/* Can't get rid of KibanaContextProvider here yet because of 'call to action when no dashboards exist' tests below */} - - null, - }, + null, }, - } as unknown as TableListViewKibanaDependencies['savedObjectsTagging'] - } - FormattedRelative={FormattedRelative} - toMountPoint={() => () => () => undefined} - > - {children} - - + }, + } as unknown as TableListViewKibanaDependencies['savedObjectsTagging'] + } + FormattedRelative={FormattedRelative} + toMountPoint={() => () => () => undefined} + > + {children} + ); }; const component = mount(, { wrappingComponent }); - return { component, props, services }; + return { component, props }; } describe('after fetch', () => { @@ -89,14 +75,14 @@ describe('after fetch', () => { }); test('renders call to action when no dashboards exist', async () => { - const services = makeDefaultServices(); - services.savedDashboards.find = () => { - return Promise.resolve({ - total: 0, - hits: [], - }); - }; - const { component } = mountWith({ services }); + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findSavedObjects as jest.Mock + ).mockResolvedValue({ + total: 0, + hits: [], + }); + + const { component } = mountWith({}); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); // Ensure the state changes are reflected @@ -105,18 +91,18 @@ describe('after fetch', () => { }); test('renders call to action with continue when no dashboards exist but one is in progress', async () => { - const services = makeDefaultServices(); - services.savedDashboards.find = () => { - return Promise.resolve({ - total: 0, - hits: [], - }); - }; pluginServices.getServices().dashboardSessionStorage.getDashboardIdsWithUnsavedChanges = jest .fn() .mockReturnValueOnce([DASHBOARD_PANELS_UNSAVED_ID]) .mockReturnValue(['dashboardUnsavedOne', 'dashboardUnsavedTwo']); - const { component } = mountWith({ services }); + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findSavedObjects as jest.Mock + ).mockResolvedValue({ + total: 0, + hits: [], + }); + + const { component } = mountWith({}); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); // Ensure the state changes are reflected @@ -139,17 +125,9 @@ describe('after fetch', () => { const title = 'search by title'; const props = makeDefaultProps(); props.title = title; - pluginServices.getServices().savedObjects.client.find = () => { - return Promise.resolve({ - perPage: 10, - total: 2, - page: 0, - savedObjects: [ - { attributes: { title: `${title}_number1` }, id: 'hello there' } as SimpleSavedObject, - { attributes: { title: `${title}_number2` }, id: 'goodbye' } as SimpleSavedObject, - ], - }); - }; + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock + ).mockResolvedValue(undefined); const { component } = mountWith({ props }); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); @@ -163,14 +141,9 @@ describe('after fetch', () => { const title = 'search by title'; const props = makeDefaultProps(); props.title = title; - pluginServices.getServices().savedObjects.client.find = () => { - return Promise.resolve({ - perPage: 10, - total: 1, - page: 0, - savedObjects: [{ attributes: { title }, id: 'you_found_me' } as SimpleSavedObject], - }); - }; + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock + ).mockResolvedValue({ id: 'you_found_me' }); const { component } = mountWith({ props }); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index 40753c556a56..1e78b9430347 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -6,7 +6,10 @@ * Side Public License, v 1. */ +import useMount from 'react-use/lib/useMount'; import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; + import { EuiLink, EuiButton, @@ -15,30 +18,29 @@ import { EuiFlexItem, EuiButtonEmpty, } from '@elastic/eui'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; -import useMount from 'react-use/lib/useMount'; -import type { SavedObjectReference } from '@kbn/core/types'; -import { useExecutionContext, useKibana } from '@kbn/kibana-react-plugin/public'; +import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; +import type { SavedObjectsFindOptionsReference, SimpleSavedObject } from '@kbn/core/public'; import type { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { TableListView, type UserContentCommonSchema } from '@kbn/content-management-table-list'; -import { attemptLoadDashboardByTitle } from '../lib'; -import { DashboardAppServices, DashboardRedirect } from '../../types'; import { getDashboardBreadcrumb, - dashboardListingTable, + dashboardListingTableStrings, noItemsStrings, dashboardUnsavedListingStrings, getNewDashboardTitle, + dashboardSavedObjectErrorStrings, } from '../../dashboard_strings'; +import { DashboardConstants } from '../..'; +import { DashboardRedirect } from '../../types'; +import { pluginServices } from '../../services/plugin_services'; import { DashboardUnsavedListing } from './dashboard_unsaved_listing'; -import { confirmCreateWithUnsaved, confirmDiscardUnsavedChanges } from './confirm_overlays'; import { getDashboardListItemLink } from './get_dashboard_list_item_link'; +import { confirmCreateWithUnsaved, confirmDiscardUnsavedChanges } from './confirm_overlays'; import { DashboardAppNoDataPage, isDashboardAppInNoDataState } from '../dashboard_app_no_data'; -import { pluginServices } from '../../services/plugin_services'; import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; +import { DashboardAttributes } from '../embeddable'; const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; @@ -52,17 +54,18 @@ interface DashboardSavedObjectUserContent extends UserContentCommonSchema { } const toTableListViewSavedObject = ( - savedObject: Record + savedObject: SimpleSavedObject ): DashboardSavedObjectUserContent => { + const { title, description, timeRestore } = savedObject.attributes; return { - id: savedObject.id as string, - updatedAt: savedObject.updatedAt! as string, - references: savedObject.references as SavedObjectReference[], type: 'dashboard', + id: savedObject.id, + updatedAt: savedObject.updatedAt!, + references: savedObject.references, attributes: { - title: (savedObject.title as string) ?? '', - description: savedObject.description as string, - timeRestore: savedObject.timeRestore as boolean, + title, + description, + timeRestore, }, }; }; @@ -80,19 +83,16 @@ export const DashboardListing = ({ initialFilter, kbnUrlStateStorage, }: DashboardListingProps) => { - const { - services: { savedDashboards }, - } = useKibana(); - const { application, + data: { query }, + dashboardSessionStorage, + settings: { uiSettings }, + notifications: { toasts }, chrome: { setBreadcrumbs }, coreContext: { executionContext }, dashboardCapabilities: { showWriteControls }, - dashboardSessionStorage, - data: { query }, - savedObjects: { client }, - settings: { uiSettings }, + dashboardSavedObject: { findDashboards, savedObjectsClient }, } = pluginServices.getServices(); const [showNoDataPage, setShowNoDataPage] = useState(false); @@ -125,7 +125,7 @@ export const DashboardListing = ({ kbnUrlStateStorage ); if (title) { - attemptLoadDashboardByTitle(title).then((result) => { + findDashboards.findByTitle(title).then((result) => { if (!result) return; redirectTo({ destination: 'dashboard', @@ -138,7 +138,7 @@ export const DashboardListing = ({ return () => { stopSyncingQueryServiceStateWithUrl(); }; - }, [title, client, redirectTo, query, kbnUrlStateStorage]); + }, [title, redirectTo, query, kbnUrlStateStorage, findDashboards]); const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -262,10 +262,11 @@ export const DashboardListing = ({ const fetchItems = useCallback( (searchTerm: string, references?: SavedObjectsFindOptionsReference[]) => { - return savedDashboards - .find(searchTerm, { - hasReference: references, + return findDashboards + .findSavedObjects({ + search: searchTerm, size: listingLimit, + hasReference: references, }) .then(({ total, hits }) => { return { @@ -274,16 +275,24 @@ export const DashboardListing = ({ }; }); }, - [listingLimit, savedDashboards] + [findDashboards, listingLimit] ); const deleteItems = useCallback( - (dashboards: Array<{ id: string }>) => { - dashboards.map((d) => dashboardSessionStorage.clearState(d.id)); + async (dashboardsToDelete: Array<{ id: string }>) => { + await Promise.all( + dashboardsToDelete.map(({ id }) => { + dashboardSessionStorage.clearState(id); + return savedObjectsClient.delete(DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, id); + }) + ).catch((error) => { + toasts.addError(error, { + title: dashboardSavedObjectErrorStrings.getErrorDeletingDashboardToast(), + }); + }); setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()); - return savedDashboards.delete(dashboards.map((d) => d.id)); }, - [savedDashboards, dashboardSessionStorage] + [savedObjectsClient, dashboardSessionStorage, toasts] ); const editItem = useCallback( @@ -292,7 +301,7 @@ export const DashboardListing = ({ [redirectTo] ); - const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTable; + const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; return ( <> {showNoDataPage && ( diff --git a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.test.tsx b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.test.tsx index 1feec9bbdc42..34a78c5181b9 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.test.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.test.tsx @@ -11,87 +11,46 @@ import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n-react'; import { waitFor } from '@testing-library/react'; import { findTestSubject } from '@elastic/eui/lib/test'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { DashboardSavedObject } from '../..'; -import { DashboardAppServices } from '../../types'; -import { makeDefaultServices } from '../test_helpers'; -import { SavedObjectLoader } from '../../services/saved_object_loader'; import { DashboardUnsavedListing, DashboardUnsavedListingProps } from './dashboard_unsaved_listing'; import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; import { pluginServices } from '../../services/plugin_services'; -const mockedDashboards: { [key: string]: DashboardSavedObject } = { - dashboardUnsavedOne: { - id: `dashboardUnsavedOne`, - title: `Dashboard Unsaved One`, - } as DashboardSavedObject, - dashboardUnsavedTwo: { - id: `dashboardUnsavedTwo`, - title: `Dashboard Unsaved Two`, - } as DashboardSavedObject, - dashboardUnsavedThree: { - id: `dashboardUnsavedThree`, - title: `Dashboard Unsaved Three`, - } as DashboardSavedObject, -}; - -function makeServices(): DashboardAppServices { - const services = makeDefaultServices(); - const savedDashboards = {} as SavedObjectLoader; - savedDashboards.get = jest - .fn() - .mockImplementation((id: string) => Promise.resolve(mockedDashboards[id])); - return { - ...services, - savedDashboards, - }; -} - const makeDefaultProps = (): DashboardUnsavedListingProps => ({ redirectTo: jest.fn(), unsavedDashboardIds: ['dashboardUnsavedOne', 'dashboardUnsavedTwo', 'dashboardUnsavedThree'], refreshUnsavedDashboards: jest.fn(), }); -function mountWith({ - services: incomingServices, - props: incomingProps, -}: { - services?: DashboardAppServices; - props?: DashboardUnsavedListingProps; -}) { - const services = incomingServices ?? makeServices(); +function mountWith({ props: incomingProps }: { props?: DashboardUnsavedListingProps }) { const props = incomingProps ?? makeDefaultProps(); const wrappingComponent: React.FC<{ children: React.ReactNode; }> = ({ children }) => { - return ( - - {/* Only the old savedObjects service is used for `DashboardUnsavedListing`, so will need to wrap this in - `DashboardServicesProvider` instead once that is removed as part of https://github.com/elastic/kibana/pull/138774*/} - {children} - - ); + return {children}; }; const component = mount(, { wrappingComponent }); - return { component, props, services }; + return { component, props }; } describe('Unsaved listing', () => { it('Gets information for each unsaved dashboard', async () => { - const { services } = mountWith({}); + mountWith({}); await waitFor(() => { - expect(services.savedDashboards.get).toHaveBeenCalledTimes(3); + expect( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByIds + ).toHaveBeenCalledTimes(1); }); }); - it('Does not attempt to get unsaved dashboard id', async () => { + it('Does not attempt to get newly created dashboard', async () => { const props = makeDefaultProps(); props.unsavedDashboardIds = ['dashboardUnsavedOne', DASHBOARD_PANELS_UNSAVED_ID]; - const { services } = mountWith({ props }); + mountWith({ props }); await waitFor(() => { - expect(services.savedDashboards.get).toHaveBeenCalledTimes(1); + expect( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByIds + ).toHaveBeenCalledWith(['dashboardUnsavedOne']); }); }); @@ -146,14 +105,22 @@ describe('Unsaved listing', () => { }); it('removes unsaved changes from any dashboard which errors on fetch', async () => { - const services = makeServices(); + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByIds as jest.Mock + ).mockResolvedValue([ + { + id: 'failCase1', + status: 'error', + error: { error: 'oh no', message: 'bwah', statusCode: 100 }, + }, + { + id: 'failCase2', + status: 'error', + error: { error: 'oh no', message: 'bwah', statusCode: 100 }, + }, + ]); + const props = makeDefaultProps(); - services.savedDashboards.get = jest.fn().mockImplementation((id: string) => { - if (id === 'failCase1' || id === 'failCase2') { - return Promise.reject(new Error()); - } - return Promise.resolve(mockedDashboards[id]); - }); props.unsavedDashboardIds = [ 'dashboardUnsavedOne', @@ -162,7 +129,7 @@ describe('Unsaved listing', () => { 'failCase1', 'failCase2', ]; - const { component } = mountWith({ services, props }); + const { component } = mountWith({ props }); waitFor(() => { component.update(); expect(pluginServices.getServices().dashboardSessionStorage.clearState).toHaveBeenCalledWith( diff --git a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx index a5c5b1b224a3..3aa862fe3026 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_unsaved_listing.tsx @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import React, { useCallback, useEffect, useState } from 'react'; - import { EuiButtonEmpty, EuiCallOut, @@ -17,14 +15,14 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import React, { useCallback, useEffect, useState } from 'react'; -import type { DashboardSavedObject } from '../..'; -import { dashboardUnsavedListingStrings, getNewDashboardTitle } from '../../dashboard_strings'; -import type { DashboardAppServices, DashboardRedirect } from '../../types'; -import { confirmDiscardUnsavedChanges } from './confirm_overlays'; +import type { DashboardRedirect } from '../../types'; import { pluginServices } from '../../services/plugin_services'; +import { confirmDiscardUnsavedChanges } from './confirm_overlays'; +import { dashboardUnsavedListingStrings, getNewDashboardTitle } from '../../dashboard_strings'; import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; +import { DashboardAttributes } from '../embeddable'; const DashboardUnsavedItem = ({ id, @@ -102,7 +100,7 @@ const DashboardUnsavedItem = ({ }; interface UnsavedItemMap { - [key: string]: DashboardSavedObject; + [key: string]: DashboardAttributes; } export interface DashboardUnsavedListingProps { @@ -117,10 +115,9 @@ export const DashboardUnsavedListing = ({ refreshUnsavedDashboards, }: DashboardUnsavedListingProps) => { const { - services: { savedDashboards }, - } = useKibana(); - - const { dashboardSessionStorage } = pluginServices.getServices(); + dashboardSessionStorage, + dashboardSavedObject: { savedObjectsClient, findDashboards }, + } = pluginServices.getServices(); const [items, setItems] = useState({}); @@ -146,28 +143,24 @@ export const DashboardUnsavedListing = ({ return; } let canceled = false; - const dashPromises = unsavedDashboardIds - .filter((id) => id !== DASHBOARD_PANELS_UNSAVED_ID) - .map((dashboardId) => { - return (savedDashboards.get(dashboardId) as Promise).catch( - () => dashboardId - ); - }); - Promise.all(dashPromises).then((dashboards: Array) => { + const existingDashboardsWithUnsavedChanges = unsavedDashboardIds.filter( + (id) => id !== DASHBOARD_PANELS_UNSAVED_ID + ); + findDashboards.findByIds(existingDashboardsWithUnsavedChanges).then((results) => { const dashboardMap = {}; if (canceled) { return; } let hasError = false; - const newItems = dashboards.reduce((map, dashboard) => { - if (typeof dashboard === 'string') { + const newItems = results.reduce((map, result) => { + if (result.status === 'error') { hasError = true; - dashboardSessionStorage.clearState(dashboard); + dashboardSessionStorage.clearState(result.id); return map; } return { ...map, - [dashboard.id || DASHBOARD_PANELS_UNSAVED_ID]: dashboard, + [result.id || DASHBOARD_PANELS_UNSAVED_ID]: result.attributes, }; }, dashboardMap); if (hasError) { @@ -179,7 +172,13 @@ export const DashboardUnsavedListing = ({ return () => { canceled = true; }; - }, [savedDashboards, dashboardSessionStorage, refreshUnsavedDashboards, unsavedDashboardIds]); + }, [ + refreshUnsavedDashboards, + dashboardSessionStorage, + unsavedDashboardIds, + savedObjectsClient, + findDashboards, + ]); return unsavedDashboardIds.length === 0 ? null : ( <> diff --git a/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts b/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts index f28d095c9b9b..669bde5c3eb6 100644 --- a/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts +++ b/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts @@ -7,11 +7,14 @@ */ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { PersistableControlGroupInput } from '@kbn/controls-plugin/common'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { RefreshInterval } from '@kbn/data-plugin/common'; import type { Filter, Query, TimeRange } from '@kbn/es-query'; -import type { DashboardOptions, DashboardPanelMap, DashboardState } from '../../types'; +import { PersistableControlGroupInput } from '@kbn/controls-plugin/common'; + +import { DashboardPanelMap } from '../../../common'; +import type { DashboardOptions, DashboardState } from '../../types'; export const dashboardStateSlice = createSlice({ name: 'dashboardState', @@ -33,11 +36,15 @@ export const dashboardStateSlice = createSlice({ description: string; tags?: string[]; timeRestore: boolean; + timeRange?: TimeRange; + refreshInterval?: RefreshInterval; }> ) => { state.title = action.payload.title; state.description = action.payload.description; state.timeRestore = action.payload.timeRestore; + state.timeRange = action.payload.timeRange; + state.refreshInterval = action.payload.refreshInterval; if (action.payload.tags) { state.tags = action.payload.tags; } diff --git a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts b/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts deleted file mode 100644 index 69da1dbbe56a..000000000000 --- a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts +++ /dev/null @@ -1,36 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { DashboardSavedObject } from '../../saved_dashboards'; - -export function getSavedDashboardMock( - config?: Partial -): DashboardSavedObject { - const searchSource = dataPluginMock.createStartContract(); - - return { - id: '123', - title: 'my dashboard', - panelsJSON: '[]', - searchSource: searchSource.search.searchSource.create(), - copyOnSave: false, - timeRestore: false, - timeTo: 'now', - timeFrom: 'now-15m', - optionsJSON: '', - lastSavedTitle: '', - destroy: () => {}, - save: () => { - return Promise.resolve('123'); - }, - getQuery: () => ({ query: '', language: 'kuery' }), - getFilters: () => [], - ...config, - } as DashboardSavedObject; -} diff --git a/src/plugins/dashboard/public/application/test_helpers/index.ts b/src/plugins/dashboard/public/application/test_helpers/index.ts index 7c8ae86074a4..c4d149e8c10b 100644 --- a/src/plugins/dashboard/public/application/test_helpers/index.ts +++ b/src/plugins/dashboard/public/application/test_helpers/index.ts @@ -7,6 +7,4 @@ */ export { getSampleDashboardInput, getSampleDashboardPanel } from './get_sample_dashboard_input'; -export { getSavedDashboardMock } from './get_saved_dashboard_mock'; -export { makeDefaultServices } from './make_default_services'; export { setupIntersectionObserverMock } from './intersection_observer_mock'; diff --git a/src/plugins/dashboard/public/application/test_helpers/make_default_services.ts b/src/plugins/dashboard/public/application/test_helpers/make_default_services.ts deleted file mode 100644 index 3ffe9dc3b70e..000000000000 --- a/src/plugins/dashboard/public/application/test_helpers/make_default_services.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { - SavedObjectLoader, - type SavedObjectLoaderFindOptions, -} from '../../services/saved_object_loader'; -import { DashboardAppServices } from '../../types'; -import { getSavedDashboardMock } from './get_saved_dashboard_mock'; - -// TODO: Remove as part of https://github.com/elastic/kibana/pull/138774 -export function makeDefaultServices(): DashboardAppServices { - const savedDashboards = {} as SavedObjectLoader; - savedDashboards.find = (search: string, sizeOrOptions: number | SavedObjectLoaderFindOptions) => { - const size = typeof sizeOrOptions === 'number' ? sizeOrOptions : sizeOrOptions.size ?? 10; - const hits = []; - for (let i = 0; i < size; i++) { - hits.push({ - id: `dashboard${i}`, - title: `dashboard${i} - ${search} - title`, - description: `dashboard${i} desc`, - references: [], - timeRestore: true, - type: '', - url: '', - updatedAt: '', - panelsJSON: '', - lastSavedTitle: '', - }); - } - return Promise.resolve({ - total: size, - hits, - }); - }; - savedDashboards.get = jest - .fn() - .mockImplementation((id?: string) => Promise.resolve(getSavedDashboardMock({ id }))); - - return { - savedDashboards, - }; -} diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 86fd56c2ec6a..97630c48dc78 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -6,46 +6,32 @@ * Side Public License, v 1. */ -import { METRIC_TYPE } from '@kbn/analytics'; -import { Required } from '@kbn/utility-types'; -import { EuiHorizontalRule } from '@elastic/eui'; import UseUnmount from 'react-use/lib/useUnmount'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import type { OverlayRef } from '@kbn/core/public'; -import type { TopNavMenuProps } from '@kbn/navigation-plugin/public'; -import type { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; import { - AddFromLibraryButton, + withSuspense, LazyLabsFlyout, - PrimaryActionButton, + SolutionToolbar, QuickButtonGroup, QuickButtonProps, - SolutionToolbar, - withSuspense, + PrimaryActionButton, + AddFromLibraryButton, } from '@kbn/presentation-util-plugin/public'; -import type { SavedQuery } from '@kbn/data-plugin/common'; -import { isErrorEmbeddable, openAddPanelFlyout, ViewMode } from '@kbn/embeddable-plugin/public'; import { - getSavedObjectFinder, - type SaveResult, showSaveModal, + type SaveResult, + getSavedObjectFinder, } from '@kbn/saved-objects-plugin/public'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { Required } from '@kbn/utility-types'; +import { EuiHorizontalRule } from '@elastic/eui'; +import type { OverlayRef } from '@kbn/core/public'; +import type { SavedQuery } from '@kbn/data-plugin/common'; +import type { TopNavMenuProps } from '@kbn/navigation-plugin/public'; +import type { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; +import { isErrorEmbeddable, openAddPanelFlyout, ViewMode } from '@kbn/embeddable-plugin/public'; -import { saveDashboard } from '../lib'; -import { TopNavIds } from './top_nav_ids'; -import { EditorMenu } from './editor_menu'; -import { UI_SETTINGS } from '../../../common'; -import { DashboardSaveModal } from './save_modal'; -import { showCloneModal } from './show_clone_modal'; -import { ShowShareModal } from './show_share_modal'; -import { getTopNavConfig } from './get_top_nav_config'; -import { showOptionsPopover } from './show_options_popover'; -import { DashboardConstants } from '../../dashboard_constants'; -import { confirmDiscardUnsavedChanges } from '../listing/confirm_overlays'; -import type { DashboardAppState, DashboardSaveOptions, NavAction } from '../../types'; -import type { DashboardEmbedSettings, DashboardRedirect } from '../../types'; -import { getCreateVisualizationButtonTitle, unsavedChangesBadge } from '../../dashboard_strings'; import { setFullScreenMode, setHidePanelTitles, @@ -58,8 +44,21 @@ import { useDashboardDispatch, useDashboardSelector, } from '../state'; +import { TopNavIds } from './top_nav_ids'; +import { EditorMenu } from './editor_menu'; +import { UI_SETTINGS } from '../../../common'; +import { DashboardSaveModal } from './save_modal'; +import { showCloneModal } from './show_clone_modal'; +import { ShowShareModal } from './show_share_modal'; +import { getTopNavConfig } from './get_top_nav_config'; +import { showOptionsPopover } from './show_options_popover'; import { pluginServices } from '../../services/plugin_services'; +import { DashboardEmbedSettings, DashboardRedirect, DashboardState } from '../../types'; +import { confirmDiscardUnsavedChanges } from '../listing/confirm_overlays'; import { useDashboardMountContext } from '../hooks/dashboard_mount_context'; +import { DashboardConstants, getFullEditPath } from '../../dashboard_constants'; +import { DashboardAppState, DashboardSaveOptions, NavAction } from '../../types'; +import { getCreateVisualizationButtonTitle, unsavedChangesBadge } from '../../dashboard_strings'; export interface DashboardTopNavState { chromeIsVisible: boolean; @@ -70,18 +69,13 @@ export interface DashboardTopNavState { type CompleteDashboardAppState = Required< DashboardAppState, - 'getLatestDashboardState' | 'dashboardContainer' | 'savedDashboard' | 'applyFilters' + 'getLatestDashboardState' | 'dashboardContainer' >; export const isCompleteDashboardAppState = ( state: DashboardAppState ): state is CompleteDashboardAppState => { - return ( - Boolean(state.getLatestDashboardState) && - Boolean(state.dashboardContainer) && - Boolean(state.savedDashboard) && - Boolean(state.applyFilters) - ); + return Boolean(state.getLatestDashboardState) && Boolean(state.dashboardContainer); }; export interface DashboardTopNavProps { @@ -101,24 +95,28 @@ export function DashboardTopNav({ }: DashboardTopNavProps) { const { setHeaderActionMenu } = useDashboardMountContext(); const { + dashboardSavedObject: { + checkForDuplicateDashboardTitle, + saveDashboardStateToSavedObject, + savedObjectsClient, + }, chrome: { getIsVisible$: getChromeIsVisible$, recentlyAccessed: chromeRecentlyAccessed, docTitle, }, coreContext: { i18nContext }, - dashboardCapabilities, + share, + overlays, + notifications, + usageCollection, data: { query, search }, - embeddable: { getEmbeddableFactory, getEmbeddableFactories, getStateTransfer }, - initializerContext: { allowByValueEmbeddables }, navigation: { TopNavMenu }, - notifications, - overlays, - savedObjects, - savedObjectsTagging: { hasTagDecoration, hasApi }, settings: { uiSettings, theme }, - share, - usageCollection, + initializerContext: { allowByValueEmbeddables }, + dashboardCapabilities: { showWriteControls, saveQuery: showSaveQuery }, + savedObjectsTagging: { hasApi: hasSavedObjectsTagging }, + embeddable: { getEmbeddableFactory, getEmbeddableFactories, getStateTransfer }, visualizations: { get: getVisualization, getAliases: getVisTypeAliases }, } = pluginServices.getServices(); @@ -144,24 +142,18 @@ export function DashboardTopNav({ const visibleSubscription = getChromeIsVisible$().subscribe((chromeIsVisible) => { setState((s) => ({ ...s, chromeIsVisible })); }); - const { id, title, getFullEditPath } = dashboardAppState.savedDashboard; - if (id && title) { + const { savedObjectId, title, viewMode } = dashboardState; + if (savedObjectId && title) { chromeRecentlyAccessed.add( - getFullEditPath(dashboardState.viewMode === ViewMode.EDIT), + getFullEditPath(savedObjectId, viewMode === ViewMode.EDIT), title, - id + savedObjectId ); } return () => { visibleSubscription.unsubscribe(); }; - }, [ - getChromeIsVisible$, - chromeRecentlyAccessed, - allowByValueEmbeddables, - dashboardState.viewMode, - dashboardAppState.savedDashboard, - ]); + }, [allowByValueEmbeddables, chromeRecentlyAccessed, dashboardState, getChromeIsVisible$]); const addFromLibrary = useCallback(() => { if (!isErrorEmbeddable(dashboardAppState.dashboardContainer)) { @@ -173,7 +165,7 @@ export function DashboardTopNav({ getFactory: getEmbeddableFactory, notifications, overlays, - SavedObjectFinder: getSavedObjectFinder(savedObjects, uiSettings), + SavedObjectFinder: getSavedObjectFinder({ client: savedObjectsClient }, uiSettings), reportUiCounter: usageCollection.reportUiCounter, theme, }), @@ -181,14 +173,14 @@ export function DashboardTopNav({ } }, [ dashboardAppState.dashboardContainer, + usageCollection.reportUiCounter, getEmbeddableFactories, getEmbeddableFactory, + savedObjectsClient, notifications, - savedObjects, overlays, - theme, uiSettings, - usageCollection, + theme, ]); const createNewVisType = useCallback( @@ -258,48 +250,71 @@ export function DashboardTopNav({ onTitleDuplicate, isTitleDuplicateConfirmed, }: DashboardSaveOptions): Promise => { + const { + timefilter: { timefilter }, + } = query; + const saveOptions = { confirmOverwrite: false, isTitleDuplicateConfirmed, onTitleDuplicate, + saveAsCopy: newCopyOnSave, }; - const stateFromSaveModal = { + const stateFromSaveModal: Pick< + DashboardState, + 'title' | 'description' | 'timeRestore' | 'timeRange' | 'refreshInterval' | 'tags' + > = { title: newTitle, + tags: [] as string[], description: newDescription, timeRestore: newTimeRestore, - tags: [] as string[], + timeRange: newTimeRestore ? timefilter.getTime() : undefined, + refreshInterval: newTimeRestore ? timefilter.getRefreshInterval() : undefined, }; - if (hasApi && newTags) { - // remove `hasAPI` once the savedObjectsTagging service is optional + if (hasSavedObjectsTagging && newTags) { + // remove `hasSavedObjectsTagging` once the savedObjectsTagging service is optional stateFromSaveModal.tags = newTags; } - dashboardAppState.savedDashboard.copyOnSave = newCopyOnSave; - const saveResult = await saveDashboard({ + if ( + !(await checkForDuplicateDashboardTitle({ + title: newTitle, + onTitleDuplicate, + lastSavedTitle: currentState.title, + copyOnSave: newCopyOnSave, + isTitleDuplicateConfirmed, + })) + ) { + // do not save if title is duplicate and is unconfirmed + return {}; + } + + const saveResult = await saveDashboardStateToSavedObject({ redirectTo, saveOptions, - savedDashboard: dashboardAppState.savedDashboard, currentState: { ...currentState, ...stateFromSaveModal }, }); if (saveResult.id && !saveResult.redirected) { dispatchDashboardStateChange(setStateFromSaveModal(stateFromSaveModal)); - dashboardAppState.updateLastSavedState?.(); - docTitle.change(stateFromSaveModal.title); + setTimeout(() => { + /** + * set timeout so dashboard state subject can update with the new title before updating the last saved state. + * TODO: Remove this timeout once the last saved state is also handled in Redux. + **/ + dashboardAppState.updateLastSavedState?.(); + docTitle.change(stateFromSaveModal.title); + }, 1); } - return saveResult.id ? { id: saveResult.id } : { error: saveResult.error }; + return saveResult.id ? { id: saveResult.id } : { error: new Error(saveResult.error) }; }; - const lastDashboardId = dashboardAppState.savedDashboard.id; - const savedTags = hasTagDecoration?.(dashboardAppState.savedDashboard) - ? dashboardAppState.savedDashboard.getTags() - : []; - const currentTagsSet = new Set([...savedTags, ...currentState.tags]); + const lastDashboardId = currentState.savedObjectId; const dashboardSaveModal = ( {}} - tags={Array.from(currentTagsSet)} + tags={currentState.tags} title={currentState.title} timeRestore={currentState.timeRestore} description={currentState.description} @@ -309,24 +324,25 @@ export function DashboardTopNav({ closeAllFlyouts(); showSaveModal(dashboardSaveModal, i18nContext); }, [ + saveDashboardStateToSavedObject, + checkForDuplicateDashboardTitle, dispatchDashboardStateChange, - hasApi, - hasTagDecoration, + hasSavedObjectsTagging, dashboardAppState, - i18nContext, - docTitle, closeAllFlyouts, + i18nContext, redirectTo, + docTitle, + query, ]); const runQuickSave = useCallback(async () => { setState((s) => ({ ...s, isSaveInProgress: true })); const currentState = dashboardAppState.getLatestDashboardState(); - const saveResult = await saveDashboard({ + const saveResult = await saveDashboardStateToSavedObject({ redirectTo, currentState, saveOptions: {}, - savedDashboard: dashboardAppState.savedDashboard, }); if (saveResult.id && !saveResult.redirected) { dashboardAppState.updateLastSavedState?.(); @@ -336,7 +352,7 @@ export function DashboardTopNav({ if (!mounted) return; setState((s) => ({ ...s, isSaveInProgress: false })); }, DashboardConstants.CHANGE_CHECK_DEBOUNCE); - }, [dashboardAppState, redirectTo, mounted]); + }, [dashboardAppState, saveDashboardStateToSavedObject, redirectTo, mounted]); const runClone = useCallback(() => { const currentState = dashboardAppState.getLatestDashboardState(); @@ -345,22 +361,33 @@ export function DashboardTopNav({ isTitleDuplicateConfirmed: boolean, onTitleDuplicate: () => void ) => { - dashboardAppState.savedDashboard.copyOnSave = true; - const saveOptions = { - confirmOverwrite: false, - isTitleDuplicateConfirmed, - onTitleDuplicate, - }; - const saveResult = await saveDashboard({ + if ( + !(await checkForDuplicateDashboardTitle({ + title: newTitle, + onTitleDuplicate, + lastSavedTitle: currentState.title, + copyOnSave: true, + isTitleDuplicateConfirmed, + })) + ) { + // do not clone if title is duplicate and is unconfirmed + return {}; + } + + const saveResult = await saveDashboardStateToSavedObject({ redirectTo, - saveOptions, - savedDashboard: dashboardAppState.savedDashboard, + saveOptions: { saveAsCopy: true }, currentState: { ...currentState, title: newTitle }, }); return saveResult.id ? { id: saveResult.id } : { error: saveResult.error }; }; showCloneModal({ onClone, title: currentState.title }); - }, [dashboardAppState, redirectTo]); + }, [ + checkForDuplicateDashboardTitle, + saveDashboardStateToSavedObject, + dashboardAppState, + redirectTo, + ]); const showOptions = useCallback( (anchorElement: HTMLElement) => { @@ -394,7 +421,6 @@ export function DashboardTopNav({ ShowShareModal({ anchorElement, currentDashboardState: currentState, - savedDashboard: dashboardAppState.savedDashboard, isDirty: Boolean(dashboardAppState.hasUnsavedChanges), }); }, @@ -442,7 +468,7 @@ export function DashboardTopNav({ }); const getNavBarProps = (): TopNavMenuProps => { - const { hasUnsavedChanges, savedDashboard } = dashboardAppState; + const { hasUnsavedChanges } = dashboardAppState; const shouldShowNavBarComponent = (forceShow: boolean): boolean => (forceShow || state.chromeIsVisible) && !dashboardState.fullScreenMode; @@ -464,11 +490,11 @@ export function DashboardTopNav({ dashboardAppState.getLatestDashboardState().viewMode, dashboardTopNavActions, { - showWriteControls: dashboardCapabilities.showWriteControls, - isDirty: Boolean(dashboardAppState.hasUnsavedChanges), - isSaveInProgress: state.isSaveInProgress, - isNewDashboard: !savedDashboard.id, isLabsEnabled, + showWriteControls, + isSaveInProgress: state.isSaveInProgress, + isNewDashboard: !dashboardState.savedObjectId, + isDirty: Boolean(dashboardAppState.hasUnsavedChanges), } ); @@ -485,22 +511,22 @@ export function DashboardTopNav({ return { badges, - appName: 'dashboard', - config: showTopNavMenu ? topNav : undefined, - className: isFullScreenMode ? 'kbnTopNavMenu-isFullScreen' : undefined, screenTitle, - showSearchBar, showQueryBar, + showSearchBar, + showFilterBar, + showSaveQuery, showQueryInput, showDatePicker, - showFilterBar, - setMenuMountPoint: embedSettings ? undefined : setHeaderActionMenu, - indexPatterns: dashboardAppState.dataViews, - showSaveQuery: dashboardCapabilities.saveQuery, + appName: 'dashboard', useDefaultBehaviors: true, + visible: printMode !== true, savedQuery: state.savedQuery, savedQueryId: dashboardState.savedQuery, - visible: printMode !== true, + indexPatterns: dashboardAppState.dataViews, + config: showTopNavMenu ? topNav : undefined, + setMenuMountPoint: embedSettings ? undefined : setHeaderActionMenu, + className: isFullScreenMode ? 'kbnTopNavMenu-isFullScreen' : undefined, onQuerySubmit: (_payload, isUpdate) => { if (isUpdate === false) { dashboardAppState.$triggerDashboardRefresh.next({ force: true }); diff --git a/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx b/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx index ad9d85926a94..7e76ef1789a3 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx @@ -10,10 +10,9 @@ import { Capabilities } from '@kbn/core/public'; import { DashboardState } from '../../types'; import { DashboardAppLocatorParams } from '../..'; +import { pluginServices } from '../../services/plugin_services'; import { stateToRawDashboardState } from '../lib/convert_dashboard_state'; -import { getSavedDashboardMock } from '../test_helpers'; import { showPublicUrlSwitch, ShowShareModal, ShowShareModalProps } from './show_share_modal'; -import { pluginServices } from '../../services/plugin_services'; describe('showPublicUrlSwitch', () => { test('returns false if "dashboard" app is not available', () => { @@ -75,9 +74,8 @@ describe('ShowShareModal', () => { .mockReturnValue(unsavedState); return { isDirty: true, - savedDashboard: getSavedDashboardMock(), anchorElement: document.createElement('div'), - currentDashboardState: { panels: {} } as unknown as DashboardState, + currentDashboardState: { panels: {} } as DashboardState, }; }; @@ -139,7 +137,7 @@ describe('ShowShareModal', () => { }); unsavedStateKeys.forEach((key) => { expect(shareLocatorParams[key]).toStrictEqual( - (rawDashboardState as Partial)[key] + (rawDashboardState as unknown as Partial)[key] ); }); }); diff --git a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx index f93061f56d6e..0f7d119427dd 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx @@ -9,28 +9,26 @@ import moment from 'moment'; import React, { ReactElement, useState } from 'react'; -import { EuiCheckboxGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { EuiCheckboxGroup } from '@elastic/eui'; import type { Capabilities } from '@kbn/core/public'; -import type { SerializableControlGroupInput } from '@kbn/controls-plugin/common'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { setStateToKbnUrl, unhashUrl } from '@kbn/kibana-utils-plugin/public'; +import type { SerializableControlGroupInput } from '@kbn/controls-plugin/common'; -import type { DashboardSavedObject } from '../..'; -import { shareModalStrings } from '../../dashboard_strings'; -import { DashboardAppLocatorParams, DASHBOARD_APP_LOCATOR } from '../../locator'; import type { DashboardState } from '../../types'; import { dashboardUrlParams } from '../dashboard_router'; -import { stateToRawDashboardState } from '../lib/convert_dashboard_state'; -import { convertPanelMapToSavedPanels } from '../lib/convert_dashboard_panels'; +import { shareModalStrings } from '../../dashboard_strings'; +import { convertPanelMapToSavedPanels } from '../../../common'; import { pluginServices } from '../../services/plugin_services'; +import { stateToRawDashboardState } from '../lib/convert_dashboard_state'; +import { DashboardAppLocatorParams, DASHBOARD_APP_LOCATOR } from '../../locator'; const showFilterBarId = 'showFilterBar'; export interface ShowShareModalProps { isDirty: boolean; anchorElement: HTMLElement; - savedDashboard: DashboardSavedObject; currentDashboardState: DashboardState; } @@ -45,7 +43,6 @@ export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => export function ShowShareModal({ isDirty, anchorElement, - savedDashboard, currentDashboardState, }: ShowShareModalProps) { const { @@ -59,6 +56,7 @@ export function ShowShareModal({ }, }, share: { toggleShareContextMenu }, + initializerContext: { kibanaVersion }, } = pluginServices.getServices(); if (!toggleShareContextMenu) return; // TODO: Make this logic cleaner once share is an optional service @@ -124,7 +122,9 @@ export function ShowShareModal({ DashboardAppLocatorParams, 'options' | 'query' | 'savedQuery' | 'filters' | 'panels' | 'controlGroupInput' > = {}; - const unsavedDashboardState = dashboardSessionStorage.getState(savedDashboard.id); + const { savedObjectId, title } = currentDashboardState; + const unsavedDashboardState = dashboardSessionStorage.getState(savedObjectId); + if (unsavedDashboardState) { unsavedStateForLocator = { query: unsavedDashboardState.query, @@ -133,13 +133,16 @@ export function ShowShareModal({ savedQuery: unsavedDashboardState.savedQuery, controlGroupInput: unsavedDashboardState.controlGroupInput as SerializableControlGroupInput, panels: unsavedDashboardState.panels - ? convertPanelMapToSavedPanels(unsavedDashboardState.panels) + ? (convertPanelMapToSavedPanels( + unsavedDashboardState.panels, + kibanaVersion + ) as DashboardAppLocatorParams['panels']) : undefined, }; } const locatorParams: DashboardAppLocatorParams = { - dashboardId: savedDashboard.id, + dashboardId: savedObjectId, preserveSavedFilters: true, refreshInterval: undefined, // We don't share refresh interval externally viewMode: ViewMode.VIEW, // For share locators we always load the dashboard in view mode @@ -161,11 +164,11 @@ export function ShowShareModal({ { useHash: false, storeInHashQuery: true }, unhashUrl(window.location.href) ), - objectId: savedDashboard.id, + objectId: savedObjectId, objectType: 'dashboard', sharingData: { title: - savedDashboard.title || + title || i18n.translate('dashboard.share.defaultDashboardTitle', { defaultMessage: 'Dashboard [{date}]', values: { date: moment().toISOString(true) }, diff --git a/src/plugins/dashboard/public/dashboard_constants.ts b/src/plugins/dashboard/public/dashboard_constants.ts index badc14ddaee6..9bbe97681032 100644 --- a/src/plugins/dashboard/public/dashboard_constants.ts +++ b/src/plugins/dashboard/public/dashboard_constants.ts @@ -6,9 +6,18 @@ * Side Public License, v 1. */ +import { ViewMode } from '@kbn/embeddable-plugin/common'; +import type { DashboardState } from './types'; + export const DASHBOARD_STATE_STORAGE_KEY = '_a'; export const GLOBAL_STATE_STORAGE_KEY = '_g'; +export const DASHBOARD_GRID_COLUMN_COUNT = 48; +export const DASHBOARD_GRID_HEIGHT = 20; +export const DEFAULT_PANEL_WIDTH = DASHBOARD_GRID_COLUMN_COUNT / 2; +export const DEFAULT_PANEL_HEIGHT = 15; +export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; + export const DashboardConstants = { LANDING_PAGE_PATH: '/list', CREATE_NEW_DASHBOARD_URL: '/create', @@ -18,11 +27,37 @@ export const DashboardConstants = { ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', DASHBOARDS_ID: 'dashboards', DASHBOARD_ID: 'dashboard', + DASHBOARD_SAVED_OBJECT_TYPE: 'dashboard', SEARCH_SESSION_ID: 'searchSessionId', CHANGE_CHECK_DEBOUNCE: 100, CHANGE_APPLY_DEBOUNCE: 50, }; +export const defaultDashboardState: DashboardState = { + viewMode: ViewMode.EDIT, // new dashboards start in edit mode. + fullScreenMode: false, + timeRestore: false, + query: { query: '', language: 'kuery' }, + description: '', + filters: [], + panels: {}, + title: '', + tags: [], + options: { + useMargins: true, + syncColors: false, + syncTooltips: false, + hidePanelTitles: false, + }, +}; + +export const getFullPath = (aliasId?: string, id?: string) => + `/app/dashboards#${createDashboardEditUrl(aliasId || id)}`; + +export const getFullEditPath = (id?: string, editMode?: boolean) => { + return `/app/dashboards#${createDashboardEditUrl(id, editMode)}`; +}; + export function createDashboardEditUrl(id?: string, editMode?: boolean) { if (!id) { return `${DashboardConstants.CREATE_NEW_DASHBOARD_URL}`; diff --git a/src/plugins/dashboard/public/dashboard_strings.ts b/src/plugins/dashboard/public/dashboard_strings.ts index 5679ac28f838..6474c7dc2bba 100644 --- a/src/plugins/dashboard/public/dashboard_strings.ts +++ b/src/plugins/dashboard/public/dashboard_strings.ts @@ -382,7 +382,7 @@ export const panelStorageErrorStrings = { }), }; -export const dashboardLoadingErrorStrings = { +export const dashboardSavedObjectErrorStrings = { getDashboardLoadError: (message: string) => i18n.translate('dashboard.loadingError.errorMessage', { defaultMessage: 'Error encountered while loading saved dashboard: {message}', @@ -393,6 +393,14 @@ export const dashboardLoadingErrorStrings = { defaultMessage: 'Unable to load dashboard: {message}', values: { message }, }), + getErrorDeletingDashboardToast: () => + i18n.translate('dashboard.deleteError.toastDescription', { + defaultMessage: 'Error encountered while deleting dashboard', + }), + getPanelTooOldError: () => + i18n.translate('dashboard.loadURLError.PanelTooOld', { + defaultMessage: 'Cannot load panels from a URL created in a version older than 7.3', + }), }; /* @@ -432,7 +440,7 @@ export const emptyScreenStrings = { /* Dashboard Listing Page */ -export const dashboardListingTable = { +export const dashboardListingTableStrings = { getEntityName: () => i18n.translate('dashboard.listing.table.entityName', { defaultMessage: 'dashboard', @@ -450,8 +458,8 @@ export const dashboardUnsavedListingStrings = { defaultMessage: 'You have unsaved changes in the following {dash}:', values: { dash: plural - ? dashboardListingTable.getEntityNamePlural() - : dashboardListingTable.getEntityName(), + ? dashboardListingTableStrings.getEntityNamePlural() + : dashboardListingTableStrings.getEntityName(), }, }), getLoadingTitle: () => diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index 7cb54db209c1..598940bbd666 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -9,11 +9,7 @@ import { PluginInitializerContext } from '@kbn/core/public'; import { DashboardPlugin } from './plugin'; -export { - DashboardContainer, - DashboardContainerFactoryDefinition, - DASHBOARD_CONTAINER_TYPE, -} from './application'; +export { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; export type { DashboardSetup, DashboardStart, DashboardFeatureFlagConfig } from './plugin'; @@ -23,7 +19,6 @@ export { cleanEmptyKeys, } from './locator'; -export type { DashboardSavedObject } from './saved_dashboards'; export type { SavedDashboardPanel, DashboardContainerInput } from './types'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/dashboard/public/locator.ts b/src/plugins/dashboard/public/locator.ts index e3cd3f159f73..a66015afcb00 100644 --- a/src/plugins/dashboard/public/locator.ts +++ b/src/plugins/dashboard/public/locator.ts @@ -94,7 +94,7 @@ export type DashboardAppLocatorParams = { /** * List of dashboard panels */ - panels?: SavedDashboardPanel[]; + panels?: Array; // used SerializableRecord here to force the GridData type to be read as serializable /** * Saved query ID diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 9d1df9d2acb1..a0d35e2c7be4 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -9,73 +9,56 @@ import { BehaviorSubject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; -import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; -import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { App, Plugin, - type CoreSetup, - type CoreStart, AppUpdater, ScopedHistory, + type CoreSetup, + type CoreStart, AppMountParameters, DEFAULT_APP_CATEGORIES, PluginInitializerContext, SavedObjectsClientContract, } from '@kbn/core/public'; -import { - CONTEXT_MENU_TRIGGER, - EmbeddableSetup, - EmbeddableStart, - PANEL_BADGE_TRIGGER, - PANEL_NOTIFICATION_TRIGGER, -} from '@kbn/embeddable-plugin/public'; import type { ScreenshotModePluginSetup, ScreenshotModePluginStart, } from '@kbn/screenshot-mode-plugin/public'; -import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; -import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; -import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/public'; -import { createKbnUrlTracker } from '@kbn/kibana-utils-plugin/public'; -import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; -import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { UsageCollectionSetup, UsageCollectionStart, } from '@kbn/usage-collection-plugin/public'; +import { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/public'; +import { createKbnUrlTracker } from '@kbn/kibana-utils-plugin/public'; + +import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; +import type { SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; +import type { VisualizationsStart } from '@kbn/visualizations-plugin/public'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { getSavedObjectFinder, type SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; -import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { - ClonePanelAction, - createDashboardContainerByValueRenderer, - DASHBOARD_CONTAINER_TYPE, - DashboardContainerFactory, + type DashboardContainerFactory, DashboardContainerFactoryDefinition, - ExpandPanelAction, - ReplacePanelAction, - UnlinkFromLibraryAction, - AddToLibraryAction, - LibraryNotificationAction, - CopyToDashboardAction, -} from './application'; -import { SavedObjectLoader } from './services/saved_object_loader'; -import { DashboardAppLocatorDefinition, DashboardAppLocator } from './locator'; -import { createSavedDashboardLoader } from './saved_dashboards'; -import { DashboardConstants } from './dashboard_constants'; -import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; -import { ExportCSVAction } from './application/actions/export_csv_action'; -import { dashboardFeatureCatalog } from './dashboard_strings'; -import { FiltersNotificationBadge } from './application/actions/filters_notification_badge'; + createDashboardContainerByValueRenderer, +} from './application/embeddable'; import type { DashboardMountContextProps } from './types'; +import { dashboardFeatureCatalog } from './dashboard_strings'; +import { type DashboardAppLocator, DashboardAppLocatorDefinition } from './locator'; +import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; +import { DashboardConstants, DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; export interface DashboardFeatureFlagConfig { allowByValueEmbeddables: boolean; @@ -118,7 +101,6 @@ export interface DashboardSetup { } export interface DashboardStart { - getSavedDashboardLoader: () => SavedObjectLoader; getDashboardContainerByValueRenderer: () => ReturnType< typeof createDashboardContainerByValueRenderer >; @@ -159,9 +141,14 @@ export class DashboardPlugin new DashboardAppLocatorDefinition({ useHashedUrl: core.uiSettings.get('state:storeInSessionStorage'), getDashboardFilterFields: async (dashboardId: string) => { - const [, , selfStart] = await core.getStartServices(); - const dashboard = await selfStart.getSavedDashboardLoader().get(dashboardId); - return dashboard?.searchSource?.getField('filter') ?? []; + const { pluginServices } = await import('./services/plugin_services'); + const { + dashboardSavedObject: { loadDashboardStateFromSavedObject }, + } = pluginServices.getServices(); + return ( + (await loadDashboardStateFromSavedObject({ id: dashboardId })).dashboardState + ?.filters ?? [] + ); }, }) ); @@ -305,58 +292,16 @@ export class DashboardPlugin } public start(core: CoreStart, plugins: DashboardStartDependencies): DashboardStart { - const { uiSettings } = core; - const { uiActions, share, presentationUtil } = plugins; - this.startDashboardKibanaServices(core, plugins, this.initializerContext).then(() => { - const clonePanelAction = new ClonePanelAction(core.savedObjects); - uiActions.registerAction(clonePanelAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id); - - const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, uiSettings); - const changeViewAction = new ReplacePanelAction(SavedObjectFinder); - uiActions.registerAction(changeViewAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction.id); - - const panelLevelFiltersNotification = new FiltersNotificationBadge(); - uiActions.registerAction(panelLevelFiltersNotification); - uiActions.attachAction(PANEL_BADGE_TRIGGER, panelLevelFiltersNotification.id); - - if (share) { - const ExportCSVPlugin = new ExportCSVAction(); - uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, ExportCSVPlugin); - } - - if (this.dashboardFeatureFlagConfig?.allowByValueEmbeddables) { - const addToLibraryAction = new AddToLibraryAction(); - uiActions.registerAction(addToLibraryAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, addToLibraryAction.id); - - const unlinkFromLibraryAction = new UnlinkFromLibraryAction(); - uiActions.registerAction(unlinkFromLibraryAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, unlinkFromLibraryAction.id); - - const libraryNotificationAction = new LibraryNotificationAction(unlinkFromLibraryAction); - uiActions.registerAction(libraryNotificationAction); - uiActions.attachAction(PANEL_NOTIFICATION_TRIGGER, libraryNotificationAction.id); - - const copyToDashboardAction = new CopyToDashboardAction(presentationUtil.ContextProvider); - uiActions.registerAction(copyToDashboardAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, copyToDashboardAction.id); - } - }); - - const expandPanelAction = new ExpandPanelAction(); // this action does't rely on any services - uiActions.registerAction(expandPanelAction); - uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id); - - const savedDashboardLoader = createSavedDashboardLoader({ - savedObjectsClient: core.savedObjects.client, - savedObjects: plugins.savedObjects, - embeddableStart: plugins.embeddable, + this.startDashboardKibanaServices(core, plugins, this.initializerContext).then(async () => { + const { buildAllDashboardActions } = await import('./application/actions'); + buildAllDashboardActions({ + core, + plugins, + allowByValueEmbeddables: this.dashboardFeatureFlagConfig?.allowByValueEmbeddables, + }); }); return { - getSavedDashboardLoader: () => savedDashboardLoader, getDashboardContainerByValueRenderer: () => { const dashboardContainerFactory = plugins.embeddable.getEmbeddableFactory(DASHBOARD_CONTAINER_TYPE); diff --git a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts b/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts deleted file mode 100644 index c23b6eb2a87e..000000000000 --- a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts +++ /dev/null @@ -1,200 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { assign, cloneDeep } from 'lodash'; -import { SavedObjectsClientContract } from '@kbn/core/public'; -import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; -import { SavedObjectAttributes, SavedObjectReference } from '@kbn/core/types'; -import { RawControlGroupAttributes } from '@kbn/controls-plugin/common'; -import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; -import { ISearchSource } from '@kbn/data-plugin/common'; -import { RefreshInterval } from '@kbn/data-plugin/public'; -import { Query, Filter } from '@kbn/es-query'; -import type { SavedObject, SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; - -import { createDashboardEditUrl } from '../dashboard_constants'; -import { extractReferences, injectReferences } from '../../common/saved_dashboard_references'; - -import { DashboardOptions } from '../types'; - -export interface DashboardSavedObject extends SavedObject { - id?: string; - timeRestore: boolean; - timeTo?: string; - timeFrom?: string; - description?: string; - panelsJSON: string; - optionsJSON?: string; - // TODO: write a migration to rid of this, it's only around for bwc. - uiStateJSON?: string; - lastSavedTitle: string; - refreshInterval?: RefreshInterval; - searchSource: ISearchSource; - getQuery(): Query; - getFilters(): Filter[]; - getFullEditPath: (editMode?: boolean) => string; - outcome?: ResolvedSimpleSavedObject['outcome']; - aliasId?: ResolvedSimpleSavedObject['alias_target_id']; - aliasPurpose?: ResolvedSimpleSavedObject['alias_purpose']; - - controlGroupInput?: Omit; -} - -const defaults = { - title: '', - hits: 0, - description: '', - panelsJSON: '[]', - optionsJSON: JSON.stringify({ - // for BWC reasons we can't default dashboards that already exist without this setting to true. - useMargins: true, - syncColors: false, - syncTooltips: false, - hidePanelTitles: false, - } as DashboardOptions), - version: 1, - timeRestore: false, - timeTo: undefined, - timeFrom: undefined, - refreshInterval: undefined, -}; - -// Used only by the savedDashboards service, usually no reason to change this -export function createSavedDashboardClass( - savedObjectStart: SavedObjectsStart, - embeddableStart: EmbeddableStart, - savedObjectsClient: SavedObjectsClientContract -): new (id: string) => DashboardSavedObject { - class SavedDashboard extends savedObjectStart.SavedObjectClass { - // save these objects with the 'dashboard' type - public static type = 'dashboard'; - - // if type:dashboard has no mapping, we push this mapping into ES - public static mapping = { - title: 'text', - hits: 'integer', - description: 'text', - panelsJSON: 'text', - optionsJSON: 'text', - version: 'integer', - timeRestore: 'boolean', - timeTo: 'keyword', - timeFrom: 'keyword', - refreshInterval: { - type: 'object', - properties: { - display: { type: 'keyword' }, - pause: { type: 'boolean' }, - section: { type: 'integer' }, - value: { type: 'integer' }, - }, - }, - controlGroupInput: { - type: 'object', - properties: { - controlStyle: { type: 'keyword' }, - panelsJSON: { type: 'text' }, - }, - }, - }; - public static fieldOrder = ['title', 'description']; - public static searchSource = true; - public showInRecentlyAccessed = true; - - public outcome?: ResolvedSimpleSavedObject['outcome']; - public aliasId?: ResolvedSimpleSavedObject['alias_target_id']; - public aliasPurpose?: ResolvedSimpleSavedObject['alias_purpose']; - - constructor(arg: { id: string; useResolve: boolean } | string) { - super({ - type: SavedDashboard.type, - mapping: SavedDashboard.mapping, - searchSource: SavedDashboard.searchSource, - extractReferences: (opts: { - attributes: SavedObjectAttributes; - references: SavedObjectReference[]; - }) => extractReferences(opts, { embeddablePersistableStateService: embeddableStart }), - injectReferences: (so: DashboardSavedObject, references: SavedObjectReference[]) => { - const newAttributes = injectReferences( - { attributes: so._serialize().attributes, references }, - { - embeddablePersistableStateService: embeddableStart, - } - ); - Object.assign(so, newAttributes); - }, - - // if this is null/undefined then the SavedObject will be assigned the defaults - id: typeof arg === 'object' ? arg.id : arg, - - // default values that will get assigned if the doc is new - defaults, - }); - - const id: string = typeof arg === 'object' ? arg.id : arg; - const useResolve = typeof arg === 'object' ? arg.useResolve : false; - - this.getFullPath = () => `/app/dashboards#${createDashboardEditUrl(this.aliasId || this.id)}`; - - // Overwrite init if we want to use resolve - if (useResolve || true) { - this.init = async () => { - const esType = SavedDashboard.type; - // ensure that the esType is defined - if (!esType) throw new Error('You must define a type name to use SavedObject objects.'); - - if (!id) { - // just assign the defaults and be done - assign(this, defaults); - await this.hydrateIndexPattern!(); - - return this; - } - - const { - outcome, - alias_target_id: aliasId, - alias_purpose: aliasPurpose, - saved_object: resp, - } = await savedObjectsClient.resolve(esType, id); - - const respMapped = { - _id: resp.id, - _type: resp.type, - _source: cloneDeep(resp.attributes), - references: resp.references, - found: !!resp._version, - }; - - this.outcome = outcome; - this.aliasId = aliasId; - this.aliasPurpose = aliasPurpose; - await this.applyESResp(respMapped); - - return this; - }; - } - } - - getQuery() { - return this.searchSource!.getOwnField('query') || { query: '', language: 'kuery' }; - } - - getFilters() { - return this.searchSource!.getOwnField('filter') || []; - } - - getFullEditPath = (editMode?: boolean) => { - return `/app/dashboards#${createDashboardEditUrl(this.id, editMode)}`; - }; - } - - // Unfortunately this throws a typescript error without the casting. I think it's due to the - // convoluted way SavedObjects are created. - return SavedDashboard as unknown as new (id: string) => DashboardSavedObject; -} diff --git a/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts b/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts deleted file mode 100644 index a154fdad9656..000000000000 --- a/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts +++ /dev/null @@ -1,36 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SavedObjectsClientContract } from '@kbn/core/public'; -import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; -import type { SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; - -import { SavedObjectLoader } from '../services/saved_object_loader'; -import { createSavedDashboardClass } from './saved_dashboard'; - -interface Services { - savedObjectsClient: SavedObjectsClientContract; - savedObjects: SavedObjectsStart; - embeddableStart: EmbeddableStart; -} - -/** - * @param services - */ -export function createSavedDashboardLoader({ - savedObjects, - savedObjectsClient, - embeddableStart, -}: Services) { - const SavedDashboard = createSavedDashboardClass( - savedObjects, - embeddableStart, - savedObjectsClient - ); - return new SavedObjectLoader(SavedDashboard, savedObjectsClient); -} diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts new file mode 100644 index 000000000000..c23a76746404 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { savedObjectsServiceMock } from '@kbn/core/public/mocks'; +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardAttributes } from '../../application'; +import { FindDashboardSavedObjectsResponse } from './lib/find_dashboard_saved_objects'; + +import { DashboardSavedObjectService } from './types'; +import { LoadDashboardFromSavedObjectReturn } from './lib/load_dashboard_state_from_saved_object'; + +type DashboardSavedObjectServiceFactory = PluginServiceFactory; + +export const dashboardSavedObjectServiceFactory: DashboardSavedObjectServiceFactory = () => { + const { client: savedObjectsClient } = savedObjectsServiceMock.createStartContract(); + return { + loadDashboardStateFromSavedObject: jest.fn().mockImplementation(() => + Promise.resolve({ + dashboardState: {}, + } as LoadDashboardFromSavedObjectReturn) + ), + saveDashboardStateToSavedObject: jest.fn(), + findDashboards: { + findSavedObjects: jest.fn().mockImplementation(({ search, size }) => { + const sizeToUse = size ?? 10; + const hits: FindDashboardSavedObjectsResponse['hits'] = []; + for (let i = 0; i < sizeToUse; i++) { + hits.push({ + type: 'dashboard', + id: `dashboard${i}`, + attributes: { + description: `dashboard${i} desc`, + title: `dashboard${i} - ${search} - title`, + }, + } as FindDashboardSavedObjectsResponse['hits'][0]); + } + return Promise.resolve({ + total: sizeToUse, + hits, + }); + }), + findByIds: jest.fn().mockImplementation(() => + Promise.resolve([ + { + id: `dashboardUnsavedOne`, + status: 'success', + attributes: { + title: `Dashboard Unsaved One`, + } as unknown as DashboardAttributes, + }, + { + id: `dashboardUnsavedTwo`, + status: 'success', + attributes: { + title: `Dashboard Unsaved Two`, + } as unknown as DashboardAttributes, + }, + { + id: `dashboardUnsavedThree`, + status: 'success', + attributes: { + title: `Dashboard Unsaved Three`, + } as unknown as DashboardAttributes, + }, + ]) + ), + findByTitle: jest.fn(), + }, + checkForDuplicateDashboardTitle: jest.fn(), + savedObjectsClient, + }; +}; diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object_service.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object_service.ts new file mode 100644 index 000000000000..7fb558309936 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object_service.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; + +import type { DashboardStartDependencies } from '../../plugin'; +import { checkForDuplicateDashboardTitle } from './lib/check_for_duplicate_dashboard_title'; +import { + findDashboardIdByTitle, + findDashboardSavedObjects, + findDashboardSavedObjectsByIds, +} from './lib/find_dashboard_saved_objects'; +import { loadDashboardStateFromSavedObject } from './lib/load_dashboard_state_from_saved_object'; +import { saveDashboardStateToSavedObject } from './lib/save_dashboard_state_to_saved_object'; +import type { DashboardSavedObjectRequiredServices, DashboardSavedObjectService } from './types'; + +export type DashboardSavedObjectServiceFactory = KibanaPluginServiceFactory< + DashboardSavedObjectService, + DashboardStartDependencies, + DashboardSavedObjectRequiredServices +>; + +export const dashboardSavedObjectServiceFactory: DashboardSavedObjectServiceFactory = ( + { coreStart }, + requiredServices +) => { + const { + savedObjects: { client: savedObjectsClient }, + } = coreStart; + + return { + loadDashboardStateFromSavedObject: ({ id, getScopedHistory }) => + loadDashboardStateFromSavedObject({ + id, + getScopedHistory, + savedObjectsClient, + ...requiredServices, + }), + saveDashboardStateToSavedObject: ({ currentState, redirectTo, saveOptions }) => + saveDashboardStateToSavedObject({ + redirectTo, + saveOptions, + currentState, + savedObjectsClient, + ...requiredServices, + }), + findDashboards: { + findSavedObjects: ({ hasReference, search, size }) => + findDashboardSavedObjects({ hasReference, search, size, savedObjectsClient }), + findByIds: (ids) => findDashboardSavedObjectsByIds(savedObjectsClient, ids), + findByTitle: (title) => findDashboardIdByTitle(title, savedObjectsClient), + }, + checkForDuplicateDashboardTitle: (props) => + checkForDuplicateDashboardTitle(props, savedObjectsClient), + savedObjectsClient, + }; +}; diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts new file mode 100644 index 000000000000..e03345e78c41 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsClientContract } from '@kbn/core/public'; + +import { DashboardConstants } from '../../..'; +import type { DashboardAttributes } from '../../../application'; + +export interface DashboardDuplicateTitleCheckProps { + title: string; + copyOnSave: boolean; + lastSavedTitle: string; + onTitleDuplicate: () => void; + isTitleDuplicateConfirmed: boolean; +} + +/** + * check for an existing dashboard with the same title in ES + * returns Promise when there is no duplicate, or runs the provided onTitleDuplicate + * function when the title already exists + */ +export async function checkForDuplicateDashboardTitle( + { + title, + copyOnSave, + lastSavedTitle, + onTitleDuplicate, + isTitleDuplicateConfirmed, + }: DashboardDuplicateTitleCheckProps, + savedObjectsClient: SavedObjectsClientContract +): Promise { + // Don't check for duplicates if user has already confirmed save with duplicate title + if (isTitleDuplicateConfirmed) { + return true; + } + + // Don't check if the user isn't updating the title, otherwise that would become very annoying to have + // to confirm the save every time, except when copyOnSave is true, then we do want to check. + if (title === lastSavedTitle && !copyOnSave) { + return true; + } + const response = await savedObjectsClient.find({ + perPage: 10, + fields: ['title'], + search: `"${title}"`, + searchFields: ['title'], + type: DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, + }); + const duplicate = response.savedObjects.find( + (obj) => obj.get('title').toLowerCase() === title.toLowerCase() + ); + if (!duplicate) { + return true; + } + onTitleDuplicate?.(); + return false; +} diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts new file mode 100644 index 000000000000..c24511f56d3e --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + SavedObjectError, + SavedObjectsClientContract, + SavedObjectsFindOptionsReference, + SimpleSavedObject, +} from '@kbn/core/public'; + +import { DashboardConstants } from '../../..'; +import type { DashboardAttributes } from '../../../application'; + +export interface FindDashboardSavedObjectsArgs { + hasReference?: SavedObjectsFindOptionsReference[]; + savedObjectsClient: SavedObjectsClientContract; + search: string; + size: number; +} + +export interface FindDashboardSavedObjectsResponse { + total: number; + hits: Array>; +} + +export async function findDashboardSavedObjects({ + savedObjectsClient, + hasReference, + search, + size, +}: FindDashboardSavedObjectsArgs): Promise { + const { total, savedObjects } = await savedObjectsClient.find({ + type: DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, + search: search ? `${search}*` : undefined, + searchFields: ['title^3', 'description'], + defaultSearchOperator: 'AND' as 'AND', + perPage: size, + hasReference, + page: 1, + }); + return { + total, + hits: savedObjects, + }; +} + +export type FindDashboardBySavedObjectIdsResult = { id: string } & ( + | { status: 'success'; attributes: DashboardAttributes } + | { status: 'error'; error: SavedObjectError } +); + +export async function findDashboardSavedObjectsByIds( + savedObjectsClient: SavedObjectsClientContract, + ids: string[] +): Promise { + const { savedObjects } = await savedObjectsClient.bulkGet( + ids.map((id) => ({ id, type: DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE })) + ); + + return savedObjects.map((savedObjectResult) => { + if (savedObjectResult.error) + return { status: 'error', error: savedObjectResult.error, id: savedObjectResult.id }; + const { attributes, id } = savedObjectResult; + return { + id, + status: 'success', + attributes: attributes as DashboardAttributes, + }; + }); +} + +export async function findDashboardIdByTitle( + title: string, + savedObjectsClient: SavedObjectsClientContract +): Promise<{ id: string } | undefined> { + const results = await savedObjectsClient.find({ + search: `"${title}"`, + searchFields: ['title'], + type: 'dashboard', + }); + // The search isn't an exact match, lets see if we can find a single exact match to use + const matchingDashboards = results.savedObjects.filter( + (dashboard) => dashboard.attributes.title.toLowerCase() === title.toLowerCase() + ); + if (matchingDashboards.length === 1) { + return { id: matchingDashboards[0].id }; + } +} diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts new file mode 100644 index 000000000000..963814013dc3 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { ReactElement } from 'react'; + +import { Filter } from '@kbn/es-query'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/public'; +import { rawControlGroupAttributesToControlGroupInput } from '@kbn/controls-plugin/common'; +import { parseSearchSourceJSON, injectSearchSourceReferences } from '@kbn/data-plugin/public'; +import { SavedObjectAttributes, SavedObjectsClientContract, ScopedHistory } from '@kbn/core/public'; + +import { migrateLegacyQuery } from '../../../application/lib/migrate_legacy_query'; + +import { + DashboardConstants, + defaultDashboardState, + createDashboardEditUrl, +} from '../../../dashboard_constants'; +import type { DashboardAttributes } from '../../../application'; +import { DashboardSavedObjectRequiredServices } from '../types'; +import { DashboardOptions, DashboardState } from '../../../types'; +import { cleanFiltersForSerialize } from '../../../application/lib'; +import { convertSavedPanelsToPanelMap, injectReferences } from '../../../../common'; + +export type LoadDashboardFromSavedObjectProps = DashboardSavedObjectRequiredServices & { + id?: string; + getScopedHistory?: () => ScopedHistory; + savedObjectsClient: SavedObjectsClientContract; +}; + +export interface LoadDashboardFromSavedObjectReturn { + redirectedToAlias?: boolean; + dashboardState?: DashboardState; + createConflictWarning?: () => ReactElement | undefined; +} + +type SuccessfulLoadDashboardFromSavedObjectReturn = LoadDashboardFromSavedObjectReturn & { + dashboardState: DashboardState; +}; + +export const dashboardStateLoadWasSuccessful = ( + incoming?: LoadDashboardFromSavedObjectReturn +): incoming is SuccessfulLoadDashboardFromSavedObjectReturn => { + return Boolean(incoming && incoming?.dashboardState && !incoming.redirectedToAlias); +}; + +export const loadDashboardStateFromSavedObject = async ({ + savedObjectsTagging, + savedObjectsClient, + getScopedHistory, + screenshotMode, + embeddable, + spaces, + data, + id, +}: LoadDashboardFromSavedObjectProps): Promise => { + const { + search: dataSearchService, + query: { queryString }, + } = data; + + /** + * This is a newly created dashboard, so there is no saved object state to load. + */ + if (!id) return { dashboardState: defaultDashboardState }; + + /** + * Load the saved object + */ + const { + outcome, + alias_purpose: aliasPurpose, + alias_target_id: aliasId, + saved_object: rawDashboardSavedObject, + } = await savedObjectsClient.resolve( + DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, + id + ); + if (!rawDashboardSavedObject._version) { + throw new SavedObjectNotFound(DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, id); + } + + /** + * Inject saved object references back into the saved object attributes + */ + const { references, attributes: rawAttributes } = rawDashboardSavedObject; + const attributes = (() => { + if (!references || references.length === 0) return rawAttributes; + return injectReferences( + { references, attributes: rawAttributes as unknown as SavedObjectAttributes }, + { + embeddablePersistableStateService: embeddable, + } + ) as unknown as DashboardAttributes; + })(); + + /** + * Handle saved object resolve alias outcome by redirecting + */ + const scopedHistory = getScopedHistory?.(); + if (scopedHistory && outcome === 'aliasMatch' && id && aliasId) { + const path = scopedHistory.location.hash.replace(id, aliasId); + if (screenshotMode.isScreenshotMode()) { + scopedHistory.replace(path); + } else { + await spaces.redirectLegacyUrl?.({ path, aliasPurpose }); + } + return { redirectedToAlias: true }; + } + + /** + * Create conflict warning component if there is a saved object id conflict + */ + const createConflictWarning = + scopedHistory && outcome === 'conflict' && aliasId + ? () => + spaces.getLegacyUrlConflict?.({ + currentObjectId: id, + otherObjectId: aliasId, + otherObjectPath: `#${createDashboardEditUrl(aliasId)}${scopedHistory.location.search}`, + }) + : undefined; + + /** + * Create search source and pull filters and query from it. + */ + const searchSourceJSON = attributes.kibanaSavedObjectMeta.searchSourceJSON; + const searchSource = await (async () => { + if (!searchSourceJSON) { + return await dataSearchService.searchSource.create(); + } + try { + let searchSourceValues = parseSearchSourceJSON(searchSourceJSON); + searchSourceValues = injectSearchSourceReferences(searchSourceValues as any, references); + return await dataSearchService.searchSource.create(searchSourceValues); + } catch (error: any) { + return await dataSearchService.searchSource.create(); + } + })(); + + const filters = cleanFiltersForSerialize((searchSource?.getOwnField('filter') as Filter[]) ?? []); + + const query = migrateLegacyQuery( + searchSource?.getOwnField('query') || queryString.getDefaultQuery() // TODO SAVED DASHBOARDS determine if migrateLegacyQuery is still needed + ); + + const { + refreshInterval, + description, + timeRestore, + optionsJSON, + panelsJSON, + timeFrom, + timeTo, + title, + } = attributes; + + const timeRange = + timeRestore && timeFrom && timeTo + ? { + from: timeFrom, + to: timeTo, + } + : undefined; + + /** + * Parse panels and options from JSON + */ + const options: DashboardOptions = optionsJSON ? JSON.parse(optionsJSON) : undefined; + const panels = convertSavedPanelsToPanelMap(panelsJSON ? JSON.parse(panelsJSON) : []); + + return { + createConflictWarning, + dashboardState: { + ...defaultDashboardState, + + savedObjectId: id, + refreshInterval, + timeRestore, + description, + timeRange, + options, + filters, + panels, + title, + query, + + viewMode: ViewMode.VIEW, // dashboards loaded from saved object default to view mode. If it was edited recently, the view mode from session storage will override this. + tags: savedObjectsTagging.getTagIdsFromReferences?.(references) ?? [], + + controlGroupInput: + attributes.controlGroupInput && + rawControlGroupAttributesToControlGroupInput(attributes.controlGroupInput), + }, + }; +}; diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts new file mode 100644 index 000000000000..11c6988d22f9 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts @@ -0,0 +1,182 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pick } from 'lodash'; + +import { isFilterPinned } from '@kbn/es-query'; +import { SavedObjectsClientContract } from '@kbn/core/public'; +import { SavedObjectAttributes } from '@kbn/core-saved-objects-common'; + +import { extractSearchSourceReferences, RefreshInterval } from '@kbn/data-plugin/public'; +import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; + +import type { DashboardAttributes } from '../../../application'; +import { DashboardSavedObjectRequiredServices } from '../types'; +import { DashboardConstants } from '../../../dashboard_constants'; +import { convertTimeToUTCString } from '../../../application/lib'; +import { DashboardRedirect, DashboardState } from '../../../types'; +import { dashboardSaveToastStrings } from '../../../dashboard_strings'; +import { convertPanelMapToSavedPanels, extractReferences } from '../../../../common'; +import { serializeControlGroupInput } from '../../../application/lib/dashboard_control_group'; + +export type SavedDashboardSaveOpts = SavedObjectSaveOpts & { saveAsCopy?: boolean }; + +export type SaveDashboardProps = DashboardSavedObjectRequiredServices & { + currentState: DashboardState; + redirectTo: DashboardRedirect; + saveOptions: SavedDashboardSaveOpts; + savedObjectsClient: SavedObjectsClientContract; +}; + +export interface SaveDashboardReturn { + id?: string; + error?: string; + redirected?: boolean; +} + +export const saveDashboardStateToSavedObject = async ({ + data, + redirectTo, + embeddable, + saveOptions, + currentState, + savedObjectsClient, + savedObjectsTagging, + dashboardSessionStorage, + notifications: { toasts }, + initializerContext: { kibanaVersion }, +}: SaveDashboardProps): Promise => { + const { + search: dataSearchService, + query: { + timefilter: { timefilter }, + }, + } = data; + + const { + tags, + query, + title, + panels, + filters, + options, + timeRestore, + description, + savedObjectId, + controlGroupInput, + } = currentState; + + /** + * Stringify filters and query into search source JSON + */ + const { searchSourceJSON, searchSourceReferences } = await (async () => { + const searchSource = await dataSearchService.searchSource.create(); + searchSource.setField( + 'filter', // save only unpinned filters + filters.filter((filter) => !isFilterPinned(filter)) + ); + searchSource.setField('query', query); + + const rawSearchSourceFields = searchSource.getSerializedFields(); + const [fields, references] = extractSearchSourceReferences(rawSearchSourceFields); + return { searchSourceReferences: references, searchSourceJSON: JSON.stringify(fields) }; + })(); + + /** + * Stringify options and panels + */ + const optionsJSON = JSON.stringify(options); + const panelsJSON = JSON.stringify(convertPanelMapToSavedPanels(panels, kibanaVersion)); + + /** + * Parse global time filter settings + */ + const { from, to } = timefilter.getTime(); + const timeFrom = timeRestore ? convertTimeToUTCString(from) : undefined; + const timeTo = timeRestore ? convertTimeToUTCString(to) : undefined; + const refreshInterval = timeRestore + ? (pick(timefilter.getRefreshInterval(), [ + 'display', + 'pause', + 'section', + 'value', + ]) as RefreshInterval) + : undefined; + + const rawDashboardAttributes: DashboardAttributes = { + controlGroupInput: serializeControlGroupInput(controlGroupInput), + kibanaSavedObjectMeta: { searchSourceJSON }, + refreshInterval, + timeRestore, + optionsJSON, + description, + panelsJSON, + timeFrom, + title, + timeTo, + version: 1, // todo - where does version come from? Why is it needed? + }; + + /** + * Extract references from raw attributes and tags into the references array. + */ + const { attributes, references: dashboardReferences } = extractReferences( + { + attributes: rawDashboardAttributes as unknown as SavedObjectAttributes, + references: searchSourceReferences, + }, + { embeddablePersistableStateService: embeddable } + ); + const references = savedObjectsTagging.updateTagsReferences + ? savedObjectsTagging.updateTagsReferences(dashboardReferences, tags) + : dashboardReferences; + + /** + * Save the saved object using the saved objects client + */ + const idToSaveTo = saveOptions.saveAsCopy ? undefined : savedObjectId; + try { + const { id: newId } = await savedObjectsClient.create( + DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE, + attributes, + { + id: idToSaveTo, + overwrite: true, + references, + } + ); + + if (newId) { + toasts.addSuccess({ + title: dashboardSaveToastStrings.getSuccessString(currentState.title), + 'data-test-subj': 'saveDashboardSuccess', + }); + + /** + * If the dashboard id has been changed, redirect to the new ID to keep the url param in sync. + */ + if (newId !== savedObjectId) { + dashboardSessionStorage.clearState(savedObjectId); + redirectTo({ + id: newId, + editMode: true, + useReplace: true, + destination: 'dashboard', + }); + return { redirected: true, id: newId }; + } + } + return { id: newId }; + } catch (error) { + toasts.addDanger({ + title: dashboardSaveToastStrings.getFailureString(currentState.title, error.message), + 'data-test-subj': 'saveDashboardFailure', + }); + return { error }; + } +}; diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/types.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/types.ts new file mode 100644 index 000000000000..dd817c751aa8 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/types.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsClientContract } from '@kbn/core/public'; + +import { DashboardDataService } from '../data/types'; +import { DashboardSpacesService } from '../spaces/types'; +import { DashboardEmbeddableService } from '../embeddable/types'; +import { DashboardNotificationsService } from '../notifications/types'; +import { DashboardScreenshotModeService } from '../screenshot_mode/types'; +import { DashboardInitializerContextService } from '../initializer_context/types'; +import { DashboardSavedObjectsTaggingService } from '../saved_objects_tagging/types'; +import { DashboardSessionStorageServiceType } from '../dashboard_session_storage/types'; + +import { + LoadDashboardFromSavedObjectProps, + LoadDashboardFromSavedObjectReturn, +} from './lib/load_dashboard_state_from_saved_object'; +import { + SaveDashboardProps, + SaveDashboardReturn, +} from './lib/save_dashboard_state_to_saved_object'; +import { + FindDashboardBySavedObjectIdsResult, + FindDashboardSavedObjectsArgs, + FindDashboardSavedObjectsResponse, +} from './lib/find_dashboard_saved_objects'; +import { DashboardDuplicateTitleCheckProps } from './lib/check_for_duplicate_dashboard_title'; + +export interface DashboardSavedObjectRequiredServices { + screenshotMode: DashboardScreenshotModeService; + embeddable: DashboardEmbeddableService; + spaces: DashboardSpacesService; + data: DashboardDataService; + initializerContext: DashboardInitializerContextService; + notifications: DashboardNotificationsService; + savedObjectsTagging: DashboardSavedObjectsTaggingService; + dashboardSessionStorage: DashboardSessionStorageServiceType; +} + +export interface DashboardSavedObjectService { + loadDashboardStateFromSavedObject: ( + props: Pick + ) => Promise; + + saveDashboardStateToSavedObject: ( + props: Pick + ) => Promise; + findDashboards: { + findSavedObjects: ( + props: Pick + ) => Promise; + findByIds: (ids: string[]) => Promise; + findByTitle: (title: string) => Promise<{ id: string } | undefined>; + }; + checkForDuplicateDashboardTitle: (meta: DashboardDuplicateTitleCheckProps) => Promise; + savedObjectsClient: SavedObjectsClientContract; +} diff --git a/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts b/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts index 18f952d4620e..a1a6b2973664 100644 --- a/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts +++ b/src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts @@ -16,9 +16,13 @@ export const embeddableServiceFactory: EmbeddableServiceFactory = () => { const pluginMock = embeddablePluginMock.createStartContract(); return { - getEmbeddableFactory: pluginMock.getEmbeddableFactory, getEmbeddableFactories: pluginMock.getEmbeddableFactories, + getEmbeddableFactory: pluginMock.getEmbeddableFactory, getStateTransfer: pluginMock.getStateTransfer, + getAllMigrations: pluginMock.getAllMigrations, EmbeddablePanel: pluginMock.EmbeddablePanel, + telemetry: pluginMock.telemetry, + extract: pluginMock.extract, + inject: pluginMock.inject, }; }; diff --git a/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts b/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts index 258c11f697bc..c796bbde2d7d 100644 --- a/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts +++ b/src/plugins/dashboard/public/services/embeddable/embeddable_service.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ +import { pick } from 'lodash'; + import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; + import type { DashboardStartDependencies } from '../../plugin'; import type { DashboardEmbeddableService } from './types'; @@ -15,14 +18,16 @@ export type EmbeddableServiceFactory = KibanaPluginServiceFactory< DashboardStartDependencies >; export const embeddableServiceFactory: EmbeddableServiceFactory = ({ startPlugins }) => { - const { - embeddable: { getEmbeddableFactory, getEmbeddableFactories, getStateTransfer, EmbeddablePanel }, - } = startPlugins; + const { embeddable } = startPlugins; - return { - getEmbeddableFactory, - getEmbeddableFactories, - getStateTransfer, - EmbeddablePanel, - }; + return pick(embeddable, [ + 'getEmbeddableFactory', + 'getEmbeddableFactories', + 'getStateTransfer', + 'EmbeddablePanel', + 'getAllMigrations', + 'telemetry', + 'extract', + 'inject', + ]); }; diff --git a/src/plugins/dashboard/public/services/embeddable/types.ts b/src/plugins/dashboard/public/services/embeddable/types.ts index ef24db7c2e62..40d0ae02bfa7 100644 --- a/src/plugins/dashboard/public/services/embeddable/types.ts +++ b/src/plugins/dashboard/public/services/embeddable/types.ts @@ -8,9 +8,14 @@ import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; -export interface DashboardEmbeddableService { - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; - getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; - getStateTransfer: EmbeddableStart['getStateTransfer']; - EmbeddablePanel: EmbeddableStart['EmbeddablePanel']; -} +export type DashboardEmbeddableService = Pick< + EmbeddableStart, + | 'getEmbeddableFactories' + | 'getEmbeddableFactory' + | 'getAllMigrations' + | 'getStateTransfer' + | 'EmbeddablePanel' + | 'telemetry' + | 'extract' + | 'inject' +>; diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts index c703b8b6767a..b8c39909dd61 100644 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ b/src/plugins/dashboard/public/services/plugin_services.stub.ts @@ -29,7 +29,6 @@ import { initializerContextServiceFactory } from './initializer_context/initiali import { navigationServiceFactory } from './navigation/navigation.stub'; import { notificationsServiceFactory } from './notifications/notifications.stub'; import { overlaysServiceFactory } from './overlays/overlays.stub'; -import { savedObjectsServiceFactory } from './saved_objects/saved_objects.stub'; import { savedObjectsTaggingServiceFactory } from './saved_objects_tagging/saved_objects_tagging.stub'; import { screenshotModeServiceFactory } from './screenshot_mode/screenshot_mode.stub'; import { settingsServiceFactory } from './settings/settings.stub'; @@ -38,8 +37,10 @@ import { usageCollectionServiceFactory } from './usage_collection/usage_collecti import { spacesServiceFactory } from './spaces/spaces.stub'; import { urlForwardingServiceFactory } from './url_forwarding/url_fowarding.stub'; import { visualizationsServiceFactory } from './visualizations/visualizations.stub'; +import { dashboardSavedObjectServiceFactory } from './dashboard_saved_object/dashboard_saved_object.stub'; export const providers: PluginServiceProviders = { + dashboardSavedObject: new PluginServiceProvider(dashboardSavedObjectServiceFactory), analytics: new PluginServiceProvider(analyticsServiceFactory), application: new PluginServiceProvider(applicationServiceFactory), chrome: new PluginServiceProvider(chromeServiceFactory), @@ -55,7 +56,6 @@ export const providers: PluginServiceProviders = { navigation: new PluginServiceProvider(navigationServiceFactory), notifications: new PluginServiceProvider(notificationsServiceFactory), overlays: new PluginServiceProvider(overlaysServiceFactory), - savedObjects: new PluginServiceProvider(savedObjectsServiceFactory), savedObjectsTagging: new PluginServiceProvider(savedObjectsTaggingServiceFactory), screenshotMode: new PluginServiceProvider(screenshotModeServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts index 421b5e75c482..b4ee1b566a8a 100644 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ b/src/plugins/dashboard/public/services/plugin_services.ts @@ -30,7 +30,6 @@ import { navigationServiceFactory } from './navigation/navigation_service'; import { notificationsServiceFactory } from './notifications/notifications_service'; import { overlaysServiceFactory } from './overlays/overlays_service'; import { screenshotModeServiceFactory } from './screenshot_mode/screenshot_mode_service'; -import { savedObjectsServiceFactory } from './saved_objects/saved_objects_service'; import { savedObjectsTaggingServiceFactory } from './saved_objects_tagging/saved_objects_tagging_service'; import { settingsServiceFactory } from './settings/settings_service'; import { shareServiceFactory } from './share/share_services'; @@ -39,17 +38,29 @@ import { urlForwardingServiceFactory } from './url_forwarding/url_forwarding_ser import { visualizationsServiceFactory } from './visualizations/visualizations_service'; import { usageCollectionServiceFactory } from './usage_collection/usage_collection_service'; import { analyticsServiceFactory } from './analytics/analytics_service'; +import { dashboardSavedObjectServiceFactory } from './dashboard_saved_object/dashboard_saved_object_service'; const providers: PluginServiceProviders = { + dashboardSavedObject: new PluginServiceProvider(dashboardSavedObjectServiceFactory, [ + 'dashboardSessionStorage', + 'savedObjectsTagging', + 'initializerContext', + 'screenshotMode', + 'notifications', + 'embeddable', + 'spaces', + 'data', + ]), + dashboardSessionStorage: new PluginServiceProvider(dashboardSessionStorageServiceFactory, [ + 'notifications', + 'spaces', + ]), + analytics: new PluginServiceProvider(analyticsServiceFactory), application: new PluginServiceProvider(applicationServiceFactory), chrome: new PluginServiceProvider(chromeServiceFactory), coreContext: new PluginServiceProvider(coreContextServiceFactory), dashboardCapabilities: new PluginServiceProvider(dashboardCapabilitiesServiceFactory), - dashboardSessionStorage: new PluginServiceProvider(dashboardSessionStorageServiceFactory, [ - 'notifications', - 'spaces', - ]), data: new PluginServiceProvider(dataServiceFactory), dataViewEditor: new PluginServiceProvider(dataViewEditorServiceFactory), documentationLinks: new PluginServiceProvider(documentationLinksServiceFactory), @@ -59,7 +70,6 @@ const providers: PluginServiceProviders SavedObject; - public type: string; - public lowercaseType: string; - public loaderProperties: Record; - - constructor( - SavedObjectClass: any, - private readonly savedObjectsClient: SavedObjectsClientContract - ) { - this.type = SavedObjectClass.type; - this.Class = SavedObjectClass; - this.lowercaseType = this.type.toLowerCase(); - - this.loaderProperties = { - name: `${this.lowercaseType}s`, - noun: upperFirst(this.type), - nouns: `${this.lowercaseType}s`, - }; - } - - /** - * Retrieve a saved object by id or create new one. - * Returns a promise that completes when the object finishes - * initializing. - * @param opts - * @returns {Promise} - */ - async get(opts?: Record | string) { - // can accept object as argument in accordance to SavedVis class - // see src/plugins/saved_objects/public/saved_object/saved_object_loader.ts - // @ts-ignore - const obj = new this.Class(opts); - return obj.init(); - } - - urlFor(id: string) { - return `#/${this.lowercaseType}/${encodeURIComponent(id)}`; - } - - async delete(ids: string | string[]) { - const idsUsed = !Array.isArray(ids) ? [ids] : ids; - - const deletions = idsUsed.map((id) => { - // @ts-ignore - const savedObject = new this.Class(id); - return savedObject.delete(); - }); - await Promise.all(deletions); - } - - /** - * Updates source to contain an id, url and references fields, and returns the updated - * source object. - * @param source - * @param id - * @param references - * @returns {source} The modified source object, with an id and url field. - */ - mapHitSource( - source: Record, - id: string, - references: SavedObjectReference[] = [], - updatedAt?: string - ): Record { - return { - ...source, - id, - url: this.urlFor(id), - references, - updatedAt, - }; - } - - /** - * Updates hit.attributes to contain an id and url field, and returns the updated - * attributes object. - * @param hit - * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. - */ - mapSavedObjectApiHits({ - attributes, - id, - references = [], - updatedAt, - }: { - attributes: Record; - id: string; - references?: SavedObjectReference[]; - updatedAt?: string; - }) { - return this.mapHitSource(attributes, id, references, updatedAt); - } - - /** - * TODO: Rather than use a hardcoded limit, implement pagination. See - * https://github.com/elastic/kibana/issues/8044 for reference. - * - * @param search - * @param size - * @param fields - * @returns {Promise} - */ - private findAll( - search: string = '', - { size = 100, fields, hasReference }: SavedObjectLoaderFindOptions - ) { - return this.savedObjectsClient - .find>({ - type: this.lowercaseType, - search: search ? `${search}*` : undefined, - perPage: size, - page: 1, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - fields, - hasReference, - } as SavedObjectsFindOptions) - .then((resp) => { - return { - total: resp.total, - hits: resp.savedObjects.map((savedObject) => this.mapSavedObjectApiHits(savedObject)), - }; - }); - } - - find(search: string = '', sizeOrOptions: number | SavedObjectLoaderFindOptions = 100) { - const options: SavedObjectLoaderFindOptions = - typeof sizeOrOptions === 'number' - ? { - size: sizeOrOptions, - } - : sizeOrOptions; - - return this.findAll(search, options).then((resp) => { - return { - total: resp.total, - hits: resp.hits.filter((savedObject) => !savedObject.error), - }; - }); - } -} diff --git a/src/plugins/dashboard/public/services/saved_objects/saved_objects.stub.ts b/src/plugins/dashboard/public/services/saved_objects/saved_objects.stub.ts deleted file mode 100644 index f26e36392603..000000000000 --- a/src/plugins/dashboard/public/services/saved_objects/saved_objects.stub.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { savedObjectsServiceMock } from '@kbn/core-saved-objects-browser-mocks'; -import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { DashboardSavedObjectsService } from './types'; - -type SavedObjectsServiceFactory = PluginServiceFactory; - -export const savedObjectsServiceFactory: SavedObjectsServiceFactory = () => { - const pluginMock = savedObjectsServiceMock.createStartContract(); - - return { - client: pluginMock.client, - }; -}; diff --git a/src/plugins/dashboard/public/services/saved_objects/saved_objects_service.ts b/src/plugins/dashboard/public/services/saved_objects/saved_objects_service.ts deleted file mode 100644 index 3fff4d9e1c36..000000000000 --- a/src/plugins/dashboard/public/services/saved_objects/saved_objects_service.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import type { DashboardStartDependencies } from '../../plugin'; -import type { DashboardSavedObjectsService } from './types'; - -export type SavedObjectsServiceFactory = KibanaPluginServiceFactory< - DashboardSavedObjectsService, - DashboardStartDependencies ->; - -export const savedObjectsServiceFactory: SavedObjectsServiceFactory = ({ coreStart }) => { - const { - savedObjects: { client }, - } = coreStart; - - return { - client, - }; -}; diff --git a/src/plugins/dashboard/public/services/saved_objects/types.ts b/src/plugins/dashboard/public/services/saved_objects/types.ts deleted file mode 100644 index d7d06131f32c..000000000000 --- a/src/plugins/dashboard/public/services/saved_objects/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { CoreStart } from '@kbn/core/public'; - -export interface DashboardSavedObjectsService { - client: CoreStart['savedObjects']['client']; -} diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts index d526eedbc1e4..1a1bcd6ca93b 100644 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts +++ b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging.stub.ts @@ -20,9 +20,10 @@ export const savedObjectsTaggingServiceFactory: SavedObjectsTaggingServiceFactor // I'm not defining components so that I don't have to update the snapshot of `save_modal.test` // However, if it's ever necessary, it can be done via: `components: pluginMock.components`, - getSearchBarFilter: pluginMock.getSearchBarFilter, - getTableColumnDefinition: pluginMock.getTableColumnDefinition, hasTagDecoration: pluginMock.hasTagDecoration, parseSearchQuery: pluginMock.parseSearchQuery, + getSearchBarFilter: pluginMock.getSearchBarFilter, + getTagIdsFromReferences: pluginMock.getTagIdsFromReferences, + getTableColumnDefinition: pluginMock.getTableColumnDefinition, }; }; diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts index 7e252ed79d7b..a100282b4cff 100644 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts +++ b/src/plugins/dashboard/public/services/saved_objects_tagging/saved_objects_tagging_service.ts @@ -27,19 +27,23 @@ export const savedObjectsTaggingServiceFactory: SavedObjectsTaggingServiceFactor const { ui: { components, + parseSearchQuery, + hasTagDecoration, getSearchBarFilter, + updateTagsReferences, + getTagIdsFromReferences, getTableColumnDefinition, - hasTagDecoration, - parseSearchQuery, }, } = taggingApi; return { hasApi: true, components, - getSearchBarFilter, - getTableColumnDefinition, hasTagDecoration, parseSearchQuery, + getSearchBarFilter, + updateTagsReferences, + getTagIdsFromReferences, + getTableColumnDefinition, }; }; diff --git a/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts b/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts index 803db5ff46d9..ba08a5370934 100644 --- a/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts +++ b/src/plugins/dashboard/public/services/saved_objects_tagging/types.ts @@ -12,9 +12,10 @@ export interface DashboardSavedObjectsTaggingService { hasApi: boolean; // remove this once the entire service is optional components?: SavedObjectsTaggingApi['ui']['components']; - getSearchBarFilter?: SavedObjectsTaggingApi['ui']['getSearchBarFilter']; - getTableColumnDefinition?: SavedObjectsTaggingApi['ui']['getTableColumnDefinition']; hasTagDecoration?: SavedObjectsTaggingApi['ui']['hasTagDecoration']; parseSearchQuery?: SavedObjectsTaggingApi['ui']['parseSearchQuery']; + getSearchBarFilter?: SavedObjectsTaggingApi['ui']['getSearchBarFilter']; + updateTagsReferences?: SavedObjectsTaggingApi['ui']['updateTagsReferences']; getTagIdsFromReferences?: SavedObjectsTaggingApi['ui']['getTagIdsFromReferences']; + getTableColumnDefinition?: SavedObjectsTaggingApi['ui']['getTableColumnDefinition']; } diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts index 3309ce057597..5d14b59e8a12 100644 --- a/src/plugins/dashboard/public/services/types.ts +++ b/src/plugins/dashboard/public/services/types.ts @@ -15,6 +15,7 @@ import { DashboardApplicationService } from './application/types'; import { DashboardChromeService } from './chrome/types'; import { DashboardCoreContextService } from './core_context/types'; import { DashboardCapabilitiesService } from './dashboard_capabilities/types'; +import { DashboardSavedObjectService } from './dashboard_saved_object/types'; import { DashboardSessionStorageServiceType } from './dashboard_session_storage/types'; import { DashboardDataService } from './data/types'; import { DashboardDataViewEditorService } from './data_view_editor/types'; @@ -25,7 +26,6 @@ import { DashboardInitializerContextService } from './initializer_context/types' import { DashboardNavigationService } from './navigation/types'; import { DashboardNotificationsService } from './notifications/types'; import { DashboardOverlaysService } from './overlays/types'; -import { DashboardSavedObjectsService } from './saved_objects/types'; import { DashboardSavedObjectsTaggingService } from './saved_objects_tagging/types'; import { DashboardScreenshotModeService } from './screenshot_mode/types'; import { DashboardSettingsService } from './settings/types'; @@ -39,12 +39,14 @@ export type DashboardPluginServiceParams = KibanaPluginServiceParams void; -export interface SavedDashboardPanelMap { - [key: string]: SavedDashboardPanel; -} - -export interface DashboardPanelMap { - [key: string]: DashboardPanelState; -} /** * DashboardState contains all pieces of tracked state for an individual dashboard @@ -48,11 +41,13 @@ export interface DashboardState { description: string; savedQuery?: string; timeRestore: boolean; + timeRange?: TimeRange; + savedObjectId?: string; fullScreenMode: boolean; expandedPanelId?: string; options: DashboardOptions; panels: DashboardPanelMap; - timeRange?: TimeRange; + refreshInterval?: RefreshInterval; timeslice?: [number, number]; controlGroupInput?: PersistableControlGroupInput; @@ -95,19 +90,17 @@ export interface DashboardAppState { dataViews?: DataView[]; updateLastSavedState?: () => void; resetToLastSavedState?: () => void; - savedDashboard?: DashboardSavedObject; dashboardContainer?: DashboardContainer; + createConflictWarning?: () => ReactElement | undefined; getLatestDashboardState?: () => DashboardState; $triggerDashboardRefresh: Subject<{ force?: boolean }>; $onDashboardStateChange: BehaviorSubject; - applyFilters?: (query: Query, filters: Filter[]) => void; } /** * The shared services and tools used to build a dashboard from a saved object ID. */ -// TODO: Remove reference to DashboardAppServices as part of https://github.com/elastic/kibana/pull/138774 -export type DashboardBuildContext = Pick & { +export interface DashboardBuildContext { locatorState?: DashboardAppLocatorParams; history: History; isEmbeddedExternally: boolean; @@ -118,7 +111,7 @@ export type DashboardBuildContext = Pick; $onDashboardStateChange: BehaviorSubject; executionContext?: KibanaExecutionContext; -}; +} // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type DashboardOptions = { @@ -156,8 +149,3 @@ export interface DashboardMountContextProps { onAppLeave: AppMountParameters['onAppLeave']; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; } - -// TODO: Remove DashboardAppServices as part of https://github.com/elastic/kibana/pull/138774 -export interface DashboardAppServices { - savedDashboards: SavedObjectLoader; -} diff --git a/src/plugins/dashboard/server/embeddable/dashboard_container_embeddable_factory.ts b/src/plugins/dashboard/server/embeddable/dashboard_container_embeddable_factory.ts index 31c236da607a..df183f631a3e 100644 --- a/src/plugins/dashboard/server/embeddable/dashboard_container_embeddable_factory.ts +++ b/src/plugins/dashboard/server/embeddable/dashboard_container_embeddable_factory.ts @@ -8,10 +8,7 @@ import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; -import { - createExtract, - createInject, -} from '../../common/embeddable/dashboard_container_persistable_state'; +import { createExtract, createInject } from '../../common'; export const dashboardPersistableStateServiceFactory = ( persistableStateService: EmbeddablePersistableStateService diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts deleted file mode 100644 index b3625bec3e8a..000000000000 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts +++ /dev/null @@ -1,318 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Serializable } from '@kbn/utility-types'; -import { get, flow, mapValues } from 'lodash'; -import { - SavedObjectAttributes, - SavedObjectMigrationFn, - SavedObjectMigrationMap, -} from '@kbn/core/server'; - -import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; -import { SavedObjectEmbeddableInput } from '@kbn/embeddable-plugin/common'; -import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-plugin/common'; -import { - mergeMigrationFunctionMaps, - MigrateFunction, - MigrateFunctionsObject, -} from '@kbn/kibana-utils-plugin/common'; -import { - CONTROL_GROUP_TYPE, - rawControlGroupAttributesToSerializable, - serializableToRawControlGroupAttributes, -} from '@kbn/controls-plugin/common'; -import { migrations730 } from './migrations_730'; -import { SavedDashboardPanel } from '../../common/types'; -import { migrateMatchAllQuery } from './migrate_match_all_query'; -import { DashboardDoc700To720, DashboardDoc730ToLatest } from '../../common'; -import { injectReferences, extractReferences } from '../../common/saved_dashboard_references'; -import { - convertPanelStateToSavedDashboardPanel, - convertSavedDashboardPanelToPanelState, -} from '../../common/embeddable/embeddable_saved_object_converters'; -import { replaceIndexPatternReference } from './replace_index_pattern_reference'; - -function migrateIndexPattern(doc: DashboardDoc700To720) { - const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); - if (typeof searchSourceJSON !== 'string') { - return; - } - let searchSource; - try { - searchSource = JSON.parse(searchSourceJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - return; - } - if (searchSource.index) { - searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; - doc.references.push({ - name: searchSource.indexRefName, - type: DATA_VIEW_SAVED_OBJECT_TYPE, - id: searchSource.index, - }); - delete searchSource.index; - } - if (searchSource.filter) { - searchSource.filter.forEach((filterRow: any, i: number) => { - if (!filterRow.meta || !filterRow.meta.index) { - return; - } - filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; - doc.references.push({ - name: filterRow.meta.indexRefName, - type: DATA_VIEW_SAVED_OBJECT_TYPE, - id: filterRow.meta.index, - }); - delete filterRow.meta.index; - }); - } - doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); -} - -const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { - // Set new "references" attribute - doc.references = doc.references || []; - - // Migrate index pattern - migrateIndexPattern(doc as DashboardDoc700To720); - // Migrate panels - const panelsJSON = get(doc, 'attributes.panelsJSON'); - if (typeof panelsJSON !== 'string') { - return doc as DashboardDoc700To720; - } - let panels; - try { - panels = JSON.parse(panelsJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - return doc as DashboardDoc700To720; - } - if (!Array.isArray(panels)) { - return doc as DashboardDoc700To720; - } - panels.forEach((panel, i) => { - if (!panel.type || !panel.id) { - return; - } - panel.panelRefName = `panel_${i}`; - doc.references!.push({ - name: `panel_${i}`, - type: panel.type, - id: panel.id, - }); - delete panel.type; - delete panel.id; - }); - doc.attributes.panelsJSON = JSON.stringify(panels); - return doc as DashboardDoc700To720; -}; - -/** - * In 7.8.0 we introduced dashboard drilldowns which are stored inside dashboard saved object as part of embeddable state - * In 7.11.0 we created an embeddable references/migrations system that allows to properly extract embeddable persistable state - * https://github.com/elastic/kibana/issues/71409 - * The idea of this migration is to inject all the embeddable panel references and then run the extraction again. - * As the result of the extraction: - * 1. In addition to regular `panel_` we will get new references which are extracted by `embeddablePersistableStateService` (dashboard drilldown references) - * 2. `panel_` references will be regenerated - * All other references like index-patterns are forwarded non touched - * @param deps - */ -function createExtractPanelReferencesMigration( - deps: DashboardSavedObjectTypeMigrationsDeps -): SavedObjectMigrationFn { - return (doc) => { - const references = doc.references ?? []; - - /** - * Remembering this because dashboard's extractReferences won't return those - * All other references like `panel_` will be overwritten - */ - const oldNonPanelReferences = references.filter((ref) => !ref.name.startsWith('panel_')); - - const injectedAttributes = injectReferences( - { - attributes: doc.attributes as unknown as SavedObjectAttributes, - references, - }, - { embeddablePersistableStateService: deps.embeddable } - ); - - const { attributes, references: newPanelReferences } = extractReferences( - { attributes: injectedAttributes, references: [] }, - { embeddablePersistableStateService: deps.embeddable } - ); - - return { - ...doc, - references: [...oldNonPanelReferences, ...newPanelReferences], - attributes, - }; - }; -} - -type ValueOrReferenceInput = SavedObjectEmbeddableInput & { - attributes?: Serializable; - savedVis?: Serializable; -}; - -/** - * Before 7.10, hidden panel titles were stored as a blank string on the title attribute. In 7.10, this was replaced - * with a usage of the existing hidePanelTitles key. Even though blank string titles still technically work - * in versions > 7.10, they are less explicit than using the hidePanelTitles key. This migration transforms all - * blank string titled panels to panels with the titles explicitly hidden. - */ -export const migrateExplicitlyHiddenTitles: SavedObjectMigrationFn = (doc) => { - const { attributes } = doc; - - // Skip if panelsJSON is missing - if (typeof attributes?.panelsJSON !== 'string') return doc; - - try { - const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[]; - // Same here, prevent failing saved object import if ever panels aren't an array. - if (!Array.isArray(panels)) return doc; - - const newPanels: SavedDashboardPanel[] = []; - panels.forEach((panel) => { - // Convert each panel into the dashboard panel state - const originalPanelState = - convertSavedDashboardPanelToPanelState(panel); - newPanels.push( - convertPanelStateToSavedDashboardPanel( - { - ...originalPanelState, - explicitInput: { - ...originalPanelState.explicitInput, - ...(originalPanelState.explicitInput.title === '' && - !originalPanelState.explicitInput.hidePanelTitles - ? { hidePanelTitles: true } - : {}), - }, - }, - panel.version - ) - ); - }); - return { - ...doc, - attributes: { - ...attributes, - panelsJSON: JSON.stringify(newPanels), - }, - }; - } catch { - return doc; - } -}; - -// Runs the embeddable migrations on each panel -const migrateByValuePanels = - (migrate: MigrateFunction, version: string): SavedObjectMigrationFn => - (doc: any) => { - const { attributes } = doc; - - if (attributes?.controlGroupInput) { - const controlGroupInput = rawControlGroupAttributesToSerializable( - attributes.controlGroupInput - ); - const migratedControlGroupInput = migrate({ - ...controlGroupInput, - type: CONTROL_GROUP_TYPE, - }); - attributes.controlGroupInput = - serializableToRawControlGroupAttributes(migratedControlGroupInput); - } - - // Skip if panelsJSON is missing otherwise this will cause saved object import to fail when - // importing objects without panelsJSON. At development time of this, there is no guarantee each saved - // object has panelsJSON in all previous versions of kibana. - if (typeof attributes?.panelsJSON !== 'string') { - return doc; - } - - const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[]; - // Same here, prevent failing saved object import if ever panels aren't an array. - if (!Array.isArray(panels)) { - return doc; - } - const newPanels: SavedDashboardPanel[] = []; - panels.forEach((panel) => { - // Convert each panel into a state that can be passed to EmbeddablesSetup.migrate - const originalPanelState = - convertSavedDashboardPanelToPanelState(panel); - - // saved vis is used to store by value input for Visualize. This should eventually be renamed to `attributes` to align with Lens and Maps - if ( - originalPanelState.explicitInput.attributes || - originalPanelState.explicitInput.savedVis - ) { - // If this panel is by value, migrate the state using embeddable migrations - const migratedInput = migrate({ - ...originalPanelState.explicitInput, - type: originalPanelState.type, - }); - // Convert the embeddable state back into the panel shape - newPanels.push( - convertPanelStateToSavedDashboardPanel( - { - ...originalPanelState, - explicitInput: { ...migratedInput, id: migratedInput.id as string }, - }, - version - ) - ); - } else { - newPanels.push(panel); - } - }); - return { - ...doc, - attributes: { - ...attributes, - panelsJSON: JSON.stringify(newPanels), - }, - }; - }; - -export interface DashboardSavedObjectTypeMigrationsDeps { - embeddable: EmbeddableSetup; -} - -export const createDashboardSavedObjectTypeMigrations = ( - deps: DashboardSavedObjectTypeMigrationsDeps -): SavedObjectMigrationMap => { - const embeddableMigrations = mapValues( - deps.embeddable.getAllMigrations(), - migrateByValuePanels - ) as MigrateFunctionsObject; - - const dashboardMigrations = { - /** - * We need to have this migration twice, once with a version prior to 7.0.0 once with a version - * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already - * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, - * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we - * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects - * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced - * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 - * only contained the 6.7.2 migration and not the 7.0.1 migration. - */ - '6.7.2': flow(migrateMatchAllQuery), - '7.0.0': flow(migrations700), - '7.3.0': flow(migrations730), - '7.9.3': flow(migrateMatchAllQuery), - '7.11.0': flow(createExtractPanelReferencesMigration(deps)), - '7.14.0': flow(replaceIndexPatternReference), - '7.17.3': flow(migrateExplicitlyHiddenTitles), - }; - - return mergeMigrationFunctionMaps(dashboardMigrations, embeddableMigrations); -}; diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard_saved_object.ts similarity index 97% rename from src/plugins/dashboard/server/saved_objects/dashboard.ts rename to src/plugins/dashboard/server/saved_objects/dashboard_saved_object.ts index 953852bee59c..b8474149ca87 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_saved_object.ts @@ -10,7 +10,7 @@ import { SavedObjectsType } from '@kbn/core/server'; import { createDashboardSavedObjectTypeMigrations, DashboardSavedObjectTypeMigrationsDeps, -} from './dashboard_migrations'; +} from './migrations/dashboard_saved_object_migrations'; export const createDashboardSavedObjectType = ({ migrationDeps, diff --git a/src/plugins/dashboard/server/saved_objects/index.ts b/src/plugins/dashboard/server/saved_objects/index.ts index af3de2dfca52..c16af55945f9 100644 --- a/src/plugins/dashboard/server/saved_objects/index.ts +++ b/src/plugins/dashboard/server/saved_objects/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { createDashboardSavedObjectType } from './dashboard'; +export { createDashboardSavedObjectType } from './dashboard_saved_object'; diff --git a/src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts b/src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts deleted file mode 100644 index cea39fc45b0f..000000000000 --- a/src/plugins/dashboard/server/saved_objects/is_dashboard_doc.ts +++ /dev/null @@ -1,37 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; -import { DashboardDoc730ToLatest } from '../../common'; - -function isDoc( - doc: { [key: string]: unknown } | SavedObjectUnsanitizedDoc -): doc is SavedObjectUnsanitizedDoc { - return ( - typeof doc.id === 'string' && - typeof doc.type === 'string' && - doc.attributes !== null && - typeof doc.attributes === 'object' && - doc.references !== null && - typeof doc.references === 'object' - ); -} - -export function isDashboardDoc( - doc: { [key: string]: unknown } | DashboardDoc730ToLatest -): doc is DashboardDoc730ToLatest { - if (!isDoc(doc)) { - return false; - } - - if (typeof (doc as DashboardDoc730ToLatest).attributes.panelsJSON !== 'string') { - return false; - } - - return true; -} diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.test.ts similarity index 99% rename from src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.test.ts index 0cefab5104d7..1a2655c48183 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.test.ts @@ -6,17 +6,15 @@ * Side Public License, v 1. */ -import { SavedObjectReference, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { SerializableRecord } from '@kbn/utility-types'; import { savedObjectsServiceMock } from '@kbn/core/server/mocks'; import { createEmbeddableSetupMock } from '@kbn/embeddable-plugin/server/mocks'; -import { createDashboardSavedObjectTypeMigrations } from './dashboard_migrations'; -import { DashboardDoc730ToLatest } from '../../common'; -import { - createExtract, - createInject, -} from '../../common/embeddable/dashboard_container_persistable_state'; +import { SavedObjectReference, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import { createExtract, createInject } from '../../../common'; import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; -import { SerializableRecord } from '@kbn/utility-types'; +import { createDashboardSavedObjectTypeMigrations } from './dashboard_saved_object_migrations'; +import { DashboardDoc730ToLatest } from './migrate_to_730/types'; const embeddableSetupMock = createEmbeddableSetupMock(); const extract = createExtract(embeddableSetupMock); diff --git a/src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.ts b/src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.ts new file mode 100644 index 000000000000..2f93c038065b --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/dashboard_saved_object_migrations.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { flow, mapValues } from 'lodash'; + +import { + mergeMigrationFunctionMaps, + MigrateFunctionsObject, +} from '@kbn/kibana-utils-plugin/common'; +import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; +import { SavedObjectMigrationFn, SavedObjectMigrationMap } from '@kbn/core/server'; + +import { migrations730, migrations700 } from './migrate_to_730'; +import { migrateMatchAllQuery } from './migrate_match_all_query'; +import { migrateExplicitlyHiddenTitles } from './migrate_hidden_titles'; +import { replaceIndexPatternReference } from './migrate_index_pattern_reference'; +import { migrateByValueDashboardPanels } from './migrate_by_value_dashboard_panels'; +import { createExtractPanelReferencesMigration } from './migrate_extract_panel_references'; + +export interface DashboardSavedObjectTypeMigrationsDeps { + embeddable: EmbeddableSetup; +} + +export const createDashboardSavedObjectTypeMigrations = ( + deps: DashboardSavedObjectTypeMigrationsDeps +): SavedObjectMigrationMap => { + const embeddableMigrations = mapValues( + deps.embeddable.getAllMigrations(), + migrateByValueDashboardPanels + ) as MigrateFunctionsObject; + + const dashboardMigrations = { + '6.7.2': flow(migrateMatchAllQuery), + '7.0.0': flow(migrations700), + '7.3.0': flow(migrations730), + '7.9.3': flow(migrateMatchAllQuery), + '7.11.0': flow(createExtractPanelReferencesMigration(deps)), + '7.14.0': flow(replaceIndexPatternReference), + '7.17.3': flow(migrateExplicitlyHiddenTitles), + }; + + return mergeMigrationFunctionMaps(dashboardMigrations, embeddableMigrations); +}; diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_by_value_dashboard_panels.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_by_value_dashboard_panels.ts new file mode 100644 index 000000000000..3bad12b53710 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_by_value_dashboard_panels.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + CONTROL_GROUP_TYPE, + rawControlGroupAttributesToSerializable, + serializableToRawControlGroupAttributes, +} from '@kbn/controls-plugin/common'; +import { Serializable } from '@kbn/utility-types'; +import { SavedObjectMigrationFn } from '@kbn/core/server'; +import { MigrateFunction } from '@kbn/kibana-utils-plugin/common'; +import { SavedObjectEmbeddableInput } from '@kbn/embeddable-plugin/common'; + +import { + convertPanelStateToSavedDashboardPanel, + convertSavedDashboardPanelToPanelState, + SavedDashboardPanel, +} from '../../../common'; + +type ValueOrReferenceInput = SavedObjectEmbeddableInput & { + attributes?: Serializable; + savedVis?: Serializable; +}; + +// Runs the embeddable migrations on each panel +export const migrateByValueDashboardPanels = + (migrate: MigrateFunction, version: string): SavedObjectMigrationFn => + (doc: any) => { + const { attributes } = doc; + + if (attributes?.controlGroupInput) { + const controlGroupInput = rawControlGroupAttributesToSerializable( + attributes.controlGroupInput + ); + const migratedControlGroupInput = migrate({ + ...controlGroupInput, + type: CONTROL_GROUP_TYPE, + }); + attributes.controlGroupInput = + serializableToRawControlGroupAttributes(migratedControlGroupInput); + } + + // Skip if panelsJSON is missing otherwise this will cause saved object import to fail when + // importing objects without panelsJSON. At development time of this, there is no guarantee each saved + // object has panelsJSON in all previous versions of kibana. + if (typeof attributes?.panelsJSON !== 'string') { + return doc; + } + + const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[]; + // Same here, prevent failing saved object import if ever panels aren't an array. + if (!Array.isArray(panels)) { + return doc; + } + const newPanels: SavedDashboardPanel[] = []; + panels.forEach((panel) => { + // Convert each panel into a state that can be passed to EmbeddablesSetup.migrate + const originalPanelState = + convertSavedDashboardPanelToPanelState(panel); + + // saved vis is used to store by value input for Visualize. This should eventually be renamed to `attributes` to align with Lens and Maps + if ( + originalPanelState.explicitInput.attributes || + originalPanelState.explicitInput.savedVis + ) { + // If this panel is by value, migrate the state using embeddable migrations + const migratedInput = migrate({ + ...originalPanelState.explicitInput, + type: originalPanelState.type, + }); + // Convert the embeddable state back into the panel shape + newPanels.push( + convertPanelStateToSavedDashboardPanel( + { + ...originalPanelState, + explicitInput: { ...migratedInput, id: migratedInput.id as string }, + }, + version + ) + ); + } else { + newPanels.push(panel); + } + }); + return { + ...doc, + attributes: { + ...attributes, + panelsJSON: JSON.stringify(newPanels), + }, + }; + }; diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts new file mode 100644 index 000000000000..5a8de73af988 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectAttributes, SavedObjectMigrationFn } from '@kbn/core/server'; + +import { DashboardAttributes, extractReferences, injectReferences } from '../../../common'; +import { DashboardSavedObjectTypeMigrationsDeps } from './dashboard_saved_object_migrations'; + +/** + * In 7.8.0 we introduced dashboard drilldowns which are stored inside dashboard saved object as part of embeddable state + * In 7.11.0 we created an embeddable references/migrations system that allows to properly extract embeddable persistable state + * https://github.com/elastic/kibana/issues/71409 + * The idea of this migration is to inject all the embeddable panel references and then run the extraction again. + * As the result of the extraction: + * 1. In addition to regular `panel_` we will get new references which are extracted by `embeddablePersistableStateService` (dashboard drilldown references) + * 2. `panel_` references will be regenerated + * All other references like index-patterns are forwarded non touched + * @param deps + */ +export function createExtractPanelReferencesMigration( + deps: DashboardSavedObjectTypeMigrationsDeps +): SavedObjectMigrationFn { + return (doc) => { + const references = doc.references ?? []; + + /** + * Remembering this because dashboard's extractReferences won't return those + * All other references like `panel_` will be overwritten + */ + const oldNonPanelReferences = references.filter((ref) => !ref.name.startsWith('panel_')); + + const injectedAttributes = injectReferences( + { + attributes: doc.attributes as unknown as SavedObjectAttributes, + references, + }, + { embeddablePersistableStateService: deps.embeddable } + ); + + const { attributes, references: newPanelReferences } = extractReferences( + { attributes: injectedAttributes, references: [] }, + { embeddablePersistableStateService: deps.embeddable } + ); + + return { + ...doc, + references: [...oldNonPanelReferences, ...newPanelReferences], + attributes, + }; + }; +} diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_hidden_titles.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_hidden_titles.ts new file mode 100644 index 000000000000..8a9a91723120 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_hidden_titles.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectMigrationFn } from '@kbn/core/server'; +import { EmbeddableInput } from '@kbn/embeddable-plugin/common'; + +import { + convertSavedDashboardPanelToPanelState, + convertPanelStateToSavedDashboardPanel, + SavedDashboardPanel, +} from '../../../common'; + +/** + * Before 7.10, hidden panel titles were stored as a blank string on the title attribute. In 7.10, this was replaced + * with a usage of the existing hidePanelTitles key. Even though blank string titles still technically work + * in versions > 7.10, they are less explicit than using the hidePanelTitles key. This migration transforms all + * blank string titled panels to panels with the titles explicitly hidden. + */ +export const migrateExplicitlyHiddenTitles: SavedObjectMigrationFn = (doc) => { + const { attributes } = doc; + + // Skip if panelsJSON is missing + if (typeof attributes?.panelsJSON !== 'string') return doc; + + try { + const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[]; + // Same here, prevent failing saved object import if ever panels aren't an array. + if (!Array.isArray(panels)) return doc; + + const newPanels: SavedDashboardPanel[] = []; + panels.forEach((panel) => { + // Convert each panel into the dashboard panel state + const originalPanelState = convertSavedDashboardPanelToPanelState(panel); + newPanels.push( + convertPanelStateToSavedDashboardPanel( + { + ...originalPanelState, + explicitInput: { + ...originalPanelState.explicitInput, + ...(originalPanelState.explicitInput.title === '' && + !originalPanelState.explicitInput.hidePanelTitles + ? { hidePanelTitles: true } + : {}), + }, + }, + panel.version + ) + ); + }); + return { + ...doc, + attributes: { + ...attributes, + panelsJSON: JSON.stringify(newPanels), + }, + }; + } catch { + return doc; + } +}; diff --git a/src/plugins/dashboard/server/saved_objects/replace_index_pattern_reference.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_index_pattern_reference.test.ts similarity index 94% rename from src/plugins/dashboard/server/saved_objects/replace_index_pattern_reference.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_index_pattern_reference.test.ts index 13db82790c9d..a9682bdb8719 100644 --- a/src/plugins/dashboard/server/saved_objects/replace_index_pattern_reference.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_index_pattern_reference.test.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectMigrationContext, SavedObjectMigrationFn } from '@kbn/core/server'; -import { replaceIndexPatternReference } from './replace_index_pattern_reference'; +import { replaceIndexPatternReference } from './migrate_index_pattern_reference'; describe('replaceIndexPatternReference', () => { const savedObjectMigrationContext = null as unknown as SavedObjectMigrationContext; diff --git a/src/plugins/dashboard/server/saved_objects/replace_index_pattern_reference.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_index_pattern_reference.ts similarity index 100% rename from src/plugins/dashboard/server/saved_objects/replace_index_pattern_reference.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_index_pattern_reference.ts diff --git a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_match_all_query.test.ts similarity index 100% rename from src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_match_all_query.test.ts diff --git a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_match_all_query.ts similarity index 100% rename from src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_match_all_query.ts index 147aa47a7a6e..a7c1f0ff6bdb 100644 --- a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_match_all_query.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { SavedObjectMigrationFn } from '@kbn/core/server'; import { get } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '@kbn/data-plugin/common'; +import { SavedObjectMigrationFn } from '@kbn/core/server'; /** * This migration script is related to: diff --git a/src/plugins/dashboard/public/saved_dashboards/index.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/index.ts similarity index 73% rename from src/plugins/dashboard/public/saved_dashboards/index.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/index.ts index 7a17aa6f2c0e..2cf081358323 100644 --- a/src/plugins/dashboard/public/saved_dashboards/index.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export * from '../../common/saved_dashboard_references'; -export * from './saved_dashboard'; -export * from './saved_dashboards'; +export { migrations730 } from './migrations_730'; +export { migrations700 } from './migrations_700'; diff --git a/src/plugins/dashboard/common/migrate_to_730_panels.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.test.ts similarity index 99% rename from src/plugins/dashboard/common/migrate_to_730_panels.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.test.ts index bdcd3bf8cedc..4eacf9b93d85 100644 --- a/src/plugins/dashboard/common/migrate_to_730_panels.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.test.ts @@ -8,13 +8,14 @@ import { migratePanelsTo730 } from './migrate_to_730_panels'; import { + SavedDashboardPanel730ToLatest, + RawSavedDashboardPanel640To720, RawSavedDashboardPanelTo60, RawSavedDashboardPanel630, - RawSavedDashboardPanel640To720, RawSavedDashboardPanel610, RawSavedDashboardPanel620, -} from './bwc/types'; -import { SavedDashboardPanelTo60, SavedDashboardPanel730ToLatest } from './types'; + SavedDashboardPanelTo60, +} from './types'; test('6.0 migrates uiState, sort, scales, and gridData', async () => { const uiState = { diff --git a/src/plugins/dashboard/common/migrate_to_730_panels.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.ts similarity index 98% rename from src/plugins/dashboard/common/migrate_to_730_panels.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.ts index f40240bd7247..531a0715038d 100644 --- a/src/plugins/dashboard/common/migrate_to_730_panels.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrate_to_730_panels.ts @@ -6,25 +6,25 @@ * Side Public License, v 1. */ +import uuid from 'uuid'; +import semverSatisfies from 'semver/functions/satisfies'; + import { i18n } from '@kbn/i18n'; import type { SerializableRecord } from '@kbn/utility-types'; -import semverSatisfies from 'semver/functions/satisfies'; -import uuid from 'uuid'; + import { - GridData, - SavedDashboardPanelTo60, SavedDashboardPanel620, SavedDashboardPanel630, SavedDashboardPanel610, -} from '.'; -import { - RawSavedDashboardPanelTo60, + SavedDashboardPanelTo60, RawSavedDashboardPanel630, - RawSavedDashboardPanel640To720, - RawSavedDashboardPanel730ToLatest, RawSavedDashboardPanel610, RawSavedDashboardPanel620, -} from './bwc/types'; + RawSavedDashboardPanelTo60, + RawSavedDashboardPanel640To720, + RawSavedDashboardPanel730ToLatest, +} from './types'; +import { GridData } from '../../../../common'; const PANEL_HEIGHT_SCALE_FACTOR = 5; const PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS = 4; @@ -266,7 +266,6 @@ export function migratePanelsTo730( | RawSavedDashboardPanel620 | RawSavedDashboardPanel630 | RawSavedDashboardPanel640To720 - // We run these on post processed panels too for url BWC | SavedDashboardPanelTo60 | SavedDashboardPanel610 | SavedDashboardPanel620 diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_700.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_700.ts new file mode 100644 index 000000000000..d1954e8266d8 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_700.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { get } from 'lodash'; + +import { SavedObjectMigrationFn } from '@kbn/core/server'; +import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; + +import { DashboardDoc700To720 } from './types'; + +function migrateIndexPattern(doc: DashboardDoc700To720) { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return; + } + if (searchSource.index) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: DATA_VIEW_SAVED_OBJECT_TYPE, + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: DATA_VIEW_SAVED_OBJECT_TYPE, + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); +} + +export const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { + // Set new "references" attribute + doc.references = doc.references || []; + + // Migrate index pattern + migrateIndexPattern(doc as DashboardDoc700To720); + // Migrate panels + const panelsJSON = get(doc, 'attributes.panelsJSON'); + if (typeof panelsJSON !== 'string') { + return doc as DashboardDoc700To720; + } + let panels; + try { + panels = JSON.parse(panelsJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc as DashboardDoc700To720; + } + if (!Array.isArray(panels)) { + return doc as DashboardDoc700To720; + } + panels.forEach((panel, i) => { + if (!panel.type || !panel.id) { + return; + } + panel.panelRefName = `panel_${i}`; + doc.references!.push({ + name: `panel_${i}`, + type: panel.type, + id: panel.id, + }); + delete panel.type; + delete panel.id; + }); + doc.attributes.panelsJSON = JSON.stringify(panels); + return doc as DashboardDoc700To720; +}; diff --git a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.test.ts similarity index 96% rename from src/plugins/dashboard/server/saved_objects/migrations_730.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.test.ts index 2fc999b06749..6314546b5933 100644 --- a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.test.ts @@ -7,12 +7,17 @@ */ import { savedObjectsServiceMock } from '@kbn/core/server/mocks'; -import { createDashboardSavedObjectTypeMigrations } from './dashboard_migrations'; -import { migrations730 } from './migrations_730'; -import { DashboardDoc700To720, DashboardDoc730ToLatest, DashboardDocPre700 } from '../../common'; -import { RawSavedDashboardPanel730ToLatest } from '../../common'; import { createEmbeddableSetupMock } from '@kbn/embeddable-plugin/server/mocks'; +import { + DashboardDocPre700, + DashboardDoc700To720, + DashboardDoc730ToLatest, + RawSavedDashboardPanel730ToLatest, +} from './types'; +import { migrations730 } from './migrations_730'; +import { createDashboardSavedObjectTypeMigrations } from '../dashboard_saved_object_migrations'; + const mockContext = savedObjectsServiceMock.createMigrationContext(); const migrations = createDashboardSavedObjectTypeMigrations({ embeddable: createEmbeddableSetupMock(), diff --git a/src/plugins/dashboard/server/saved_objects/migrations_730.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts similarity index 69% rename from src/plugins/dashboard/server/saved_objects/migrations_730.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts index 3cc34d8513a4..dcd1c2f2cb87 100644 --- a/src/plugins/dashboard/server/saved_objects/migrations_730.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts @@ -7,11 +7,38 @@ */ import { inspect } from 'util'; -import { SavedObjectMigrationContext } from '@kbn/core/server'; -import { DashboardDoc730ToLatest } from '../../common'; -import { isDashboardDoc } from './is_dashboard_doc'; +import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + import { moveFiltersToQuery } from './move_filters_to_query'; -import { migratePanelsTo730, DashboardDoc700To720 } from '../../common'; +import { migratePanelsTo730 } from './migrate_to_730_panels'; +import { DashboardDoc730ToLatest, DashboardDoc700To720 } from './types'; + +function isDoc( + doc: { [key: string]: unknown } | SavedObjectUnsanitizedDoc +): doc is SavedObjectUnsanitizedDoc { + return ( + typeof doc.id === 'string' && + typeof doc.type === 'string' && + doc.attributes !== null && + typeof doc.attributes === 'object' && + doc.references !== null && + typeof doc.references === 'object' + ); +} + +export function isDashboardDoc( + doc: { [key: string]: unknown } | DashboardDoc730ToLatest +): doc is DashboardDoc730ToLatest { + if (!isDoc(doc)) { + return false; + } + + if (typeof (doc as DashboardDoc730ToLatest).attributes.panelsJSON !== 'string') { + return false; + } + + return true; +} export const migrations730 = (doc: DashboardDoc700To720, { log }: SavedObjectMigrationContext) => { if (!isDashboardDoc(doc)) { diff --git a/src/plugins/dashboard/server/saved_objects/move_filters_to_query.test.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/move_filters_to_query.test.ts similarity index 100% rename from src/plugins/dashboard/server/saved_objects/move_filters_to_query.test.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/move_filters_to_query.test.ts diff --git a/src/plugins/dashboard/server/saved_objects/move_filters_to_query.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/move_filters_to_query.ts similarity index 100% rename from src/plugins/dashboard/server/saved_objects/move_filters_to_query.ts rename to src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/move_filters_to_query.ts diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/readme.md b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/readme.md new file mode 100644 index 000000000000..50f1b3283ca3 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/readme.md @@ -0,0 +1,3 @@ +## Legacy Pre 7.3 Migrations + +This folder contains legacy migrations that migrate dashboard saved object from any previous version into Kibana 7.3.0. The migrations in this folder need to be able to handle state from any older version of dashboard from as early as 5.0 because Saved Object Migrations did not exist, and in-place migrations were used instead. After 7.3.0, saved object migrations are in place, so it can be assumed that any saved migration that is registered there will receive state from the version before. diff --git a/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/types.ts b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/types.ts new file mode 100644 index 000000000000..2257b05c0a64 --- /dev/null +++ b/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/types.ts @@ -0,0 +1,182 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Serializable } from '@kbn/utility-types'; +import { SavedObjectReference } from '@kbn/core/server'; + +import type { + GridData, + DashboardAttributes as CurrentDashboardAttributes, // Dashboard attributes from common are the source of truth for the current version. +} from '../../../../common'; + +interface SavedObjectAttributes { + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; +} + +interface Doc { + references: SavedObjectReference[]; + attributes: Attributes; + id: string; + type: string; +} + +interface DocPre700 { + attributes: Attributes; + id: string; + type: string; +} + +interface DashboardAttributesTo720 extends SavedObjectAttributes { + panelsJSON: string; + description: string; + uiStateJSON?: string; + version: number; + timeRestore: boolean; + useMargins?: boolean; + title: string; + optionsJSON?: string; +} + +export type DashboardDoc730ToLatest = Doc; + +export type DashboardDoc700To720 = Doc; + +export type DashboardDocPre700 = DocPre700; + +// Note that these types are prefixed with `Raw` because there are some post processing steps +// that happen before the saved objects even reach the client. Namely, injecting type and id +// parameters back into the panels, where the raw saved objects actually have them stored elsewhere. +// +// Ideally, everywhere in the dashboard code would use references at the top level instead of +// embedded in the panels. The reason this is stored at the top level is so the references can be uniformly +// updated across all saved object types that have references. + +// Starting in 7.3 we introduced the possibility of embeddables existing without an id +// parameter. If there was no id, then type remains on the panel. So it either will have a name, +// or a type property. +export type RawSavedDashboardPanel730ToLatest = Pick< + RawSavedDashboardPanel640To720, + Exclude +> & { + // Should be either type, and not name (not backed by a saved object), or name but not type (backed by a + // saved object and type and id are stored on references). Had trouble with oring the two types + // because of optional properties being marked as required: https://github.com/microsoft/TypeScript/issues/20722 + readonly type?: string; + readonly name?: string; + + panelIndex: string; + panelRefName?: string; +}; + +// NOTE!! +// All of these types can actually exist in 7.2! The names are pretty confusing because we did +// in place migrations for so long. For example, `RawSavedDashboardPanelTo60` is what a panel +// created in 6.0 will look like after it's been migrated up to 7.2, *not* what it would look like in 6.0. +// That's why it actually doesn't have id or type, but has a name property, because that was a migration +// added in 7.0. + +// Hopefully since we finally have a formal saved object migration system and we can do less in place +// migrations, this will be easier to understand moving forward. + +// Starting in 6.4 we added an in-place edit on panels to remove columns and sort properties and put them +// inside the embeddable config (https://github.com/elastic/kibana/pull/17446). +// Note that this was not added as a saved object migration until 7.3, so there can still exist panels in +// this shape in v 7.2. +export type RawSavedDashboardPanel640To720 = Pick< + RawSavedDashboardPanel630, + Exclude +>; + +// In 6.3.0 we expanded the number of grid columns and rows: https://github.com/elastic/kibana/pull/16763 +// We added in-place migrations to multiply older x,y,h,w numbers. Note the typescript shape here is the same +// because it's just multiplying existing fields. +// Note that this was not added as a saved object migration until 7.3, so there can still exist panels in 7.2 +// that need to be modified. +export type RawSavedDashboardPanel630 = RawSavedDashboardPanel620; + +// In 6.2 we added an inplace migration, moving uiState into each panel's new embeddableConfig property. +// Source: https://github.com/elastic/kibana/pull/14949 +export type RawSavedDashboardPanel620 = RawSavedDashboardPanel610 & { + embeddableConfig: { [key: string]: Serializable }; + version: string; +}; + +// In 6.1 we switched from an angular grid to react grid layout (https://github.com/elastic/kibana/pull/13853) +// This used gridData instead of size_x, size_y, row and col. We also started tracking the version this panel +// was created in to make future migrations easier. +// Note that this was not added as a saved object migration until 7.3, so there can still exist panels in +// this shape in v 7.2. +export type RawSavedDashboardPanel610 = Pick< + RawSavedDashboardPanelTo60, + Exclude +> & { gridData: GridData; version: string }; + +export interface RawSavedDashboardPanelTo60 { + readonly columns?: string[]; + readonly sort?: string; + readonly size_x?: number; + readonly size_y?: number; + readonly row: number; + readonly col: number; + panelIndex?: number | string; // earlier versions allowed this to be number or string. Some very early versions seem to be missing this entirely + readonly name: string; + + // This is where custom panel titles are stored prior to Embeddable API v2 + title?: string; +} + +export type SavedDashboardPanel640To720 = Pick< + RawSavedDashboardPanel640To720, + Exclude +> & { + readonly id: string; + readonly type: string; +}; + +export type SavedDashboardPanel630 = Pick< + RawSavedDashboardPanel630, + Exclude +> & { + readonly id: string; + readonly type: string; +}; + +export type SavedDashboardPanel620 = Pick< + RawSavedDashboardPanel620, + Exclude +> & { + readonly id: string; + readonly type: string; +}; + +export type SavedDashboardPanel610 = Pick< + RawSavedDashboardPanel610, + Exclude +> & { + readonly id: string; + readonly type: string; +}; + +export type SavedDashboardPanelTo60 = Pick< + RawSavedDashboardPanelTo60, + Exclude +> & { + readonly id: string; + readonly type: string; +}; + +// id becomes optional starting in 7.3.0 +export type SavedDashboardPanel730ToLatest = Pick< + RawSavedDashboardPanel730ToLatest, + Exclude +> & { + readonly id?: string; + readonly type: string; +}; diff --git a/src/plugins/dashboard/server/usage/dashboard_telemetry.test.ts b/src/plugins/dashboard/server/usage/dashboard_telemetry.test.ts index ea981be3515f..25a4986208d3 100644 --- a/src/plugins/dashboard/server/usage/dashboard_telemetry.test.ts +++ b/src/plugins/dashboard/server/usage/dashboard_telemetry.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedDashboardPanel730ToLatest } from '../../common'; +import { SavedDashboardPanel } from '../../common'; import { getEmptyDashboardData, collectPanelsByType } from './dashboard_telemetry'; import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; import { createEmbeddablePersistableStateServiceMock } from '@kbn/embeddable-plugin/common/mocks'; @@ -18,7 +18,7 @@ const visualizationType1ByValue = { }, }, type: 'visualization', -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const visualizationType2ByValue = { embeddableConfig: { @@ -27,7 +27,7 @@ const visualizationType2ByValue = { }, }, type: 'visualization', -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const visualizationType2ByReference = { ...visualizationType2ByValue, @@ -41,7 +41,7 @@ const lensTypeAByValue = { visualizationType: 'a', }, }, -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const lensTypeAByReference = { ...lensTypeAByValue, @@ -60,7 +60,7 @@ const lensXYSeriesA = { }, }, }, -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const lensXYSeriesB = { type: 'lens', @@ -90,7 +90,7 @@ const lensXYSeriesB = { }, }, }, -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const embeddablePersistableStateService = createEmbeddablePersistableStateServiceMock(); diff --git a/src/plugins/dashboard/server/usage/dashboard_telemetry.ts b/src/plugins/dashboard/server/usage/dashboard_telemetry.ts index ce41c5083468..1e8a0192ec38 100644 --- a/src/plugins/dashboard/server/usage/dashboard_telemetry.ts +++ b/src/plugins/dashboard/server/usage/dashboard_telemetry.ts @@ -16,7 +16,7 @@ import { } from '@kbn/controls-plugin/common'; import { initializeControlGroupTelemetry } from '@kbn/controls-plugin/server'; import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; -import { SavedDashboardPanel730ToLatest } from '../../common'; +import type { SavedDashboardPanel } from '../../common'; import { TASK_ID, DashboardTelemetryTaskState } from './dashboard_telemetry_collection_task'; export interface DashboardCollectorData { panels: { @@ -55,7 +55,7 @@ export const getEmptyPanelTypeData = () => ({ }); export const collectPanelsByType = ( - panels: SavedDashboardPanel730ToLatest[], + panels: SavedDashboardPanel[], collectorData: DashboardCollectorData, embeddableService: EmbeddablePersistableStateService ) => { diff --git a/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts b/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts index 823b5fadaaea..1ca13b430858 100644 --- a/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts +++ b/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts @@ -14,17 +14,13 @@ import { TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; -import { SavedDashboardPanel730ToLatest } from '../../common'; -import { - injectReferences, - SavedObjectAttributesAndReferences, -} from '../../common/saved_dashboard_references'; import { controlsCollectorFactory, collectPanelsByType, getEmptyDashboardData, DashboardCollectorData, } from './dashboard_telemetry'; +import { injectReferences, SavedDashboardPanel } from '../../common'; // This task is responsible for running daily and aggregating all the Dashboard telemerty data // into a single document. This is an effort to make sure the load of fetching/parsing all of the @@ -32,6 +28,11 @@ import { const TELEMETRY_TASK_TYPE = 'dashboard_telemetry'; export const TASK_ID = `Dashboard-${TELEMETRY_TASK_TYPE}`; +interface SavedObjectAttributesAndReferences { + attributes: SavedObjectAttributes; + references: SavedObjectReference[]; +} + export interface DashboardTelemetryTaskState { runs: number; telemetry: DashboardCollectorData; @@ -102,7 +103,7 @@ export function dashboardTaskRunner(logger: Logger, core: CoreSetup, embeddable: try { const panels = JSON.parse( attributes.panelsJSON as string - ) as unknown as SavedDashboardPanel730ToLatest[]; + ) as unknown as SavedDashboardPanel[]; collectPanelsByType(panels, dashboardData, embeddable); } catch (e) { diff --git a/src/plugins/dashboard/server/usage/find_by_value_embeddables.test.ts b/src/plugins/dashboard/server/usage/find_by_value_embeddables.test.ts index 8a3cdd71539f..c5e8da8acbd4 100644 --- a/src/plugins/dashboard/server/usage/find_by_value_embeddables.test.ts +++ b/src/plugins/dashboard/server/usage/find_by_value_embeddables.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedDashboardPanel730ToLatest } from '../../common'; +import type { SavedDashboardPanel } from '../../common'; import { findByValueEmbeddables } from './find_by_value_embeddables'; const visualizationByValue = { @@ -14,18 +14,18 @@ const visualizationByValue = { value: 'visualization-by-value', }, type: 'visualization', -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const mapByValue = { embeddableConfig: { value: 'map-by-value', }, type: 'map', -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; const embeddableByRef = { panelRefName: 'panel_ref_1', -} as unknown as SavedDashboardPanel730ToLatest; +} as unknown as SavedDashboardPanel; describe('findByValueEmbeddables', () => { it('finds the by value embeddables for the given type', async () => { diff --git a/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts b/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts index 694fe2007f84..502ba828944d 100644 --- a/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts +++ b/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts @@ -7,7 +7,7 @@ */ import { ISavedObjectsRepository, SavedObjectAttributes } from '@kbn/core/server'; -import { SavedDashboardPanel730ToLatest } from '../../common'; +import type { SavedDashboardPanel } from '../../common'; export const findByValueEmbeddables = async ( savedObjectClient: Pick, @@ -22,7 +22,7 @@ export const findByValueEmbeddables = async ( try { return JSON.parse( dashboard.attributes.panelsJSON as string - ) as unknown as SavedDashboardPanel730ToLatest[]; + ) as unknown as SavedDashboardPanel[]; } catch (exception) { return []; } diff --git a/test/functional/apps/dashboard/group3/bwc_shared_urls.ts b/test/functional/apps/dashboard/group3/bwc_shared_urls.ts index 35d13b715c14..0b06e28af0f1 100644 --- a/test/functional/apps/dashboard/group3/bwc_shared_urls.ts +++ b/test/functional/apps/dashboard/group3/bwc_shared_urls.ts @@ -12,9 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header']); - const dashboardExpect = getService('dashboardExpect'); - const pieChart = getService('pieChart'); - const elasticChart = getService('elasticChart'); + const toasts = getService('toasts'); const browser = getService('browser'); const log = getService('log'); const queryBar = getService('queryBar'); @@ -42,11 +40,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { `legendOpen:!t))),` + `viewMode:edit)`; - const enableNewChartLibraryDebug = async () => { - await elasticChart.setNewChartUiDebugFlag(); - await queryBar.submitQuery(); - }; - describe('bwc shared urls', function describeIndexTests() { before(async function () { await PageObjects.dashboard.initTests(); @@ -81,13 +74,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`Navigating to ${url}`); await browser.get(url, true); await PageObjects.header.waitUntilLoadingHasFinished(); - await elasticChart.setNewChartUiDebugFlag(true); const query = await queryBar.getQueryString(); expect(query).to.equal('memory:>220000'); - await pieChart.expectEmptyPieChart(); - await dashboardExpect.panelCount(2); + const warningToast = await toasts.getToastElement(1); + expect(await warningToast.getVisibleText()).to.contain('Cannot load panels'); + await PageObjects.dashboard.waitForRenderComplete(); }); }); @@ -99,15 +92,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const url = `${kibanaLegacyBaseUrl}#/dashboard?${urlQuery}`; log.debug(`Navigating to ${url}`); await browser.get(url, true); - enableNewChartLibraryDebug(); await PageObjects.header.waitUntilLoadingHasFinished(); const query = await queryBar.getQueryString(); expect(query).to.equal('memory:>220000'); - await pieChart.expectPieSliceCount(5); - await dashboardExpect.panelCount(2); + const warningToast = await toasts.getToastElement(1); + expect(await warningToast.getVisibleText()).to.contain('Cannot load panels'); await PageObjects.dashboard.waitForRenderComplete(); - await dashboardExpect.selectedLegendColorCount('#F9D9F9', 5); }); it('loads a saved dashboard', async function () { @@ -120,15 +111,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`Navigating to ${url}`); await browser.get(url, true); await PageObjects.header.waitUntilLoadingHasFinished(); - enableNewChartLibraryDebug(); const query = await queryBar.getQueryString(); expect(query).to.equal('memory:>220000'); - await pieChart.expectPieSliceCount(5); - await dashboardExpect.panelCount(2); await PageObjects.dashboard.waitForRenderComplete(); - await dashboardExpect.selectedLegendColorCount('#F9D9F9', 5); }); it('loads a saved dashboard with query via dashboard_no_match', async function () { @@ -143,7 +130,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const query = await queryBar.getQueryString(); expect(query).to.equal('boop'); - await dashboardExpect.panelCount(2); await PageObjects.dashboard.waitForRenderComplete(); }); @@ -154,33 +140,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`Navigating to ${url}`); await browser.get(url, true); - await elasticChart.setNewChartUiDebugFlag(true); await PageObjects.header.waitUntilLoadingHasFinished(); - await dashboardExpect.selectedLegendColorCount('#000000', 5); }); it('back button works for old dashboards after state migrations', async () => { await PageObjects.dashboard.preserveCrossAppState(); const oldId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); await PageObjects.dashboard.waitForRenderComplete(); - await dashboardExpect.selectedLegendColorCount('#000000', 5); const url = `${kibanaLegacyBaseUrl}#/dashboard?${urlQuery}`; log.debug(`Navigating to ${url}`); await browser.get(url); - await elasticChart.setNewChartUiDebugFlag(true); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.dashboard.waitForRenderComplete(); - await dashboardExpect.selectedLegendColorCount('#F9D9F9', 5); await browser.goBack(); await PageObjects.header.waitUntilLoadingHasFinished(); const newId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); expect(newId).to.be.equal(oldId); await PageObjects.dashboard.waitForRenderComplete(); - await elasticChart.setNewChartUiDebugFlag(true); await queryBar.submitQuery(); - await dashboardExpect.selectedLegendColorCount('#000000', 5); }); }); }); diff --git a/test/functional/apps/dashboard/group3/dashboard_state.ts b/test/functional/apps/dashboard/group3/dashboard_state.ts index bc3f2ed2774a..2c79f1fd61d2 100644 --- a/test/functional/apps/dashboard/group3/dashboard_state.ts +++ b/test/functional/apps/dashboard/group3/dashboard_state.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import chroma from 'chroma-js'; -import { DEFAULT_PANEL_WIDTH } from '@kbn/dashboard-plugin/public/application/embeddable/dashboard_constants'; +import { DEFAULT_PANEL_WIDTH } from '@kbn/dashboard-plugin/public/dashboard_constants'; import { PIE_CHART_VIS_NAME, AREA_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; import { FtrProviderContext } from '../../../ftr_provider_context'; diff --git a/test/functional/apps/dashboard/group4/dashboard_empty.ts b/test/functional/apps/dashboard/group4/dashboard_empty.ts index fe5a74bebbc2..02437b068569 100644 --- a/test/functional/apps/dashboard/group4/dashboard_empty.ts +++ b/test/functional/apps/dashboard/group4/dashboard_empty.ts @@ -54,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); // create the new data view from the dashboards/create route in order to test that the dashboard is loaded properly as soon as the data view is created... - await PageObjects.common.navigateToUrl('dashboard', '/create'); + await PageObjects.common.navigateToApp('dashboard', { hash: '/create' }); const button = await testSubjects.find('createDataViewButton'); button.click(); diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 7a579f4e4f84..bad1f6bdebf2 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -12,6 +12,7 @@ export const LINE_CHART_VIS_NAME = 'Visualization漢字 LineChart'; import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; +import { CommonPageObject } from './common_page'; interface SaveDashboardOptions { /** @@ -430,6 +431,31 @@ export class DashboardPageObject extends FtrService { await this.switchToEditMode(); } + public async gotoDashboardURL({ + id, + args, + editMode, + }: { + id?: string; + editMode?: boolean; + args?: Parameters['navigateToActualUrl']>[2]; + } = {}) { + let dashboardLocation = `/create`; + if (id) { + const edit = editMode ? `?_a=(viewMode:edit)` : ''; + dashboardLocation = `/view/${id}${edit}`; + } + this.common.navigateToActualUrl('dashboard', dashboardLocation, args); + } + + public async gotoDashboardListingURL({ + args, + }: { + args?: Parameters['navigateToActualUrl']>[2]; + } = {}) { + await this.common.navigateToActualUrl('dashboard', '/list', args); + } + public async renameDashboard(dashboardName: string) { this.log.debug(`Naming dashboard ` + dashboardName); await this.testSubjects.click('dashboardRenameButton'); diff --git a/x-pack/plugins/ml/public/application/explorer/dashboard_controls/add_swimlane_to_dashboard_controls.tsx b/x-pack/plugins/ml/public/application/explorer/dashboard_controls/add_swimlane_to_dashboard_controls.tsx index 7f109defdff7..93fa7e908f35 100644 --- a/x-pack/plugins/ml/public/application/explorer/dashboard_controls/add_swimlane_to_dashboard_controls.tsx +++ b/x-pack/plugins/ml/public/application/explorer/dashboard_controls/add_swimlane_to_dashboard_controls.tsx @@ -15,7 +15,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { DashboardSavedObject } from '@kbn/dashboard-plugin/public'; +import { DashboardAttributes } from '@kbn/dashboard-plugin/common'; import type { Query } from '@kbn/es-query'; import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search'; import { getDefaultSwimlanePanelTitle } from '../../../embeddables/anomaly_swimlane/anomaly_swimlane_embeddable'; @@ -30,7 +30,7 @@ export interface DashboardItem { id: string; title: string; description: string | undefined; - attributes: DashboardSavedObject; + attributes: DashboardAttributes; } export type EuiTableProps = EuiInMemoryTableProps; diff --git a/x-pack/plugins/ml/public/application/explorer/dashboard_controls/use_dashboards_table.tsx b/x-pack/plugins/ml/public/application/explorer/dashboard_controls/use_dashboards_table.tsx index ac023017d43f..8bb7705938d8 100644 --- a/x-pack/plugins/ml/public/application/explorer/dashboard_controls/use_dashboards_table.tsx +++ b/x-pack/plugins/ml/public/application/explorer/dashboard_controls/use_dashboards_table.tsx @@ -8,7 +8,7 @@ import type { EuiInMemoryTableProps } from '@elastic/eui'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { debounce } from 'lodash'; -import type { DashboardSavedObject } from '@kbn/dashboard-plugin/public'; +import type { DashboardAttributes } from '@kbn/dashboard-plugin/common'; import { useDashboardService } from '../../services/dashboard_service'; import { useMlKibana } from '../../contexts/kibana'; @@ -16,7 +16,7 @@ export interface DashboardItem { id: string; title: string; description: string | undefined; - attributes: DashboardSavedObject; + attributes: DashboardAttributes; } export type EuiTableProps = EuiInMemoryTableProps; diff --git a/x-pack/plugins/ml/public/application/services/dashboard_service.ts b/x-pack/plugins/ml/public/application/services/dashboard_service.ts index ff7b696551f4..abd97722f86b 100644 --- a/x-pack/plugins/ml/public/application/services/dashboard_service.ts +++ b/x-pack/plugins/ml/public/application/services/dashboard_service.ts @@ -7,7 +7,8 @@ import { SavedObjectsClientContract } from '@kbn/core/public'; import { useMemo } from 'react'; -import { DashboardSavedObject, DashboardAppLocator } from '@kbn/dashboard-plugin/public'; +import { DashboardAppLocator } from '@kbn/dashboard-plugin/public'; +import type { DashboardAttributes } from '@kbn/dashboard-plugin/common'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useMlKibana } from '../contexts/kibana'; @@ -22,7 +23,7 @@ export function dashboardServiceProvider( * Fetches dashboards */ async fetchDashboards(query?: string) { - return await savedObjectClient.find({ + return await savedObjectClient.find({ type: 'dashboard', perPage: 1000, search: query ? `${query}*` : '', diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ce86f1de798d..612838bf5ea5 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -988,7 +988,6 @@ "dashboard.panel.copyToDashboard.goToDashboard": "Copier et accéder au tableau de bord", "dashboard.panel.copyToDashboard.newDashboardOptionLabel": "Nouveau tableau de bord", "dashboard.panel.copyToDashboard.title": "Copier dans le tableau de bord", - "dashboard.panel.invalidData": "Données non valides dans l'url", "dashboard.panel.LibraryNotification": "Notification de la bibliothèque Visualize", "dashboard.panel.libraryNotification.ariaLabel": "Afficher les informations de la bibliothèque et dissocier ce panneau", "dashboard.panel.libraryNotification.toolTip": "La modification de ce panneau pourrait affecter d’autres tableaux de bord. Pour modifier ce panneau uniquement, dissociez-le de la bibliothèque.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5b56cc076172..1ace6b0a7665 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -986,7 +986,6 @@ "dashboard.panel.copyToDashboard.goToDashboard": "コピーしてダッシュボードを開く", "dashboard.panel.copyToDashboard.newDashboardOptionLabel": "新規ダッシュボード", "dashboard.panel.copyToDashboard.title": "ダッシュボードにコピー", - "dashboard.panel.invalidData": "URL の無効なデータ", "dashboard.panel.LibraryNotification": "Visualize ライブラリ通知", "dashboard.panel.libraryNotification.ariaLabel": "ライブラリ情報を表示し、このパネルのリンクを解除します", "dashboard.panel.libraryNotification.toolTip": "このパネルを編集すると、他のダッシュボードに影響する場合があります。このパネルのみを変更するには、ライブラリからリンクを解除します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 09fb50c164e9..2cdc7fa51626 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -988,7 +988,6 @@ "dashboard.panel.copyToDashboard.goToDashboard": "复制并前往仪表板", "dashboard.panel.copyToDashboard.newDashboardOptionLabel": "新仪表板", "dashboard.panel.copyToDashboard.title": "复制到仪表板", - "dashboard.panel.invalidData": "url 中的数据无效", "dashboard.panel.LibraryNotification": "可视化库通知", "dashboard.panel.libraryNotification.ariaLabel": "查看库信息并取消链接此面板", "dashboard.panel.libraryNotification.toolTip": "编辑此面板可能会影响其他仪表板。要仅更改此面板,请取消其与库的链接。", diff --git a/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts index 8f7fc4fe7306..3a33f14d682a 100644 --- a/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts @@ -6,10 +6,6 @@ */ import expect from '@kbn/expect'; -import { - createDashboardEditUrl, - DashboardConstants, -} from '@kbn/dashboard-plugin/public/dashboard_constants'; import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { @@ -32,6 +28,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const savedQueryManagementComponent = getService('savedQueryManagementComponent'); const kbnServer = getService('kibanaServer'); + const navigationArgs = { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }; + describe('dashboard feature controls security', () => { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); @@ -97,14 +98,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`landing page shows "Create new Dashboard" button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardListingURL({ + args: navigationArgs, + }); await testSubjects.existOrFail('dashboardLandingPage', { timeout: config.get('timeouts.waitFor'), }); @@ -116,28 +112,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`create new dashboard shows addNew button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ args: navigationArgs }); await testSubjects.existOrFail('emptyDashboardWidget', { timeout: config.get('timeouts.waitFor'), }); }); it(`can view existing Dashboard`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-exist'), - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ + id: 'i-exist', + args: navigationArgs, + }); await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: config.get('timeouts.waitFor'), }); @@ -307,14 +292,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`landing page doesn't show "Create new Dashboard" button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardListingURL({ + args: navigationArgs, + }); await testSubjects.existOrFail('dashboardLandingPage', { timeout: config.get('timeouts.waitFor'), }); @@ -322,38 +302,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`shows read-only badge`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardListingURL({ + args: navigationArgs, + }); await globalNav.badgeExistsOrFail('Read only'); }); it(`create new dashboard shows the read only warning`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ + args: navigationArgs, + }); await testSubjects.existOrFail('dashboardEmptyReadOnly', { timeout: 20000 }); }); it(`can view existing Dashboard`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-exist'), - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ + id: 'i-exist', + args: navigationArgs, + }); await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: config.get('timeouts.waitFor'), }); @@ -438,14 +404,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`landing page doesn't show "Create new Dashboard" button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardListingURL({ + args: navigationArgs, + }); await testSubjects.existOrFail('dashboardLandingPage', { timeout: 10000 }); await testSubjects.missingOrFail('newItemButton'); }); @@ -455,26 +416,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`create new dashboard shows the read only warning`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ + args: navigationArgs, + }); await testSubjects.existOrFail('dashboardEmptyReadOnly', { timeout: 20000 }); }); it(`can view existing Dashboard`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-exist'), - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ id: 'i-exist', args: navigationArgs }); await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: 10000 }); }); @@ -552,50 +501,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`landing page shows 403`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardListingURL({ + args: navigationArgs, + }); await PageObjects.error.expectForbidden(); }); it(`create new dashboard shows 403`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ args: navigationArgs }); await PageObjects.error.expectForbidden(); }); it(`edit dashboard for object which doesn't exist shows 403`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-dont-exist'), - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ id: 'i-dont-exist', args: navigationArgs }); await PageObjects.error.expectForbidden(); }); it(`edit dashboard for object which exists shows 403`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-exist'), - { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); + await PageObjects.dashboard.gotoDashboardURL({ id: 'i-exist', args: navigationArgs }); await PageObjects.error.expectForbidden(); }); }); diff --git a/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts index a702b7a716a1..ef8c83ec667b 100644 --- a/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts @@ -6,10 +6,6 @@ */ import expect from '@kbn/expect'; -import { - createDashboardEditUrl, - DashboardConstants, -} from '@kbn/dashboard-plugin/public/dashboard_constants'; import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { @@ -53,15 +49,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`landing page shows "Create new Dashboard" button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.LANDING_PAGE_PATH, - { + await PageObjects.dashboard.gotoDashboardListingURL({ + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await testSubjects.existOrFail('dashboardLandingPage', { timeout: config.get('timeouts.waitFor'), }); @@ -69,30 +63,27 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`create new dashboard shows addNew button`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { + await PageObjects.dashboard.gotoDashboardURL({ + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await testSubjects.existOrFail('emptyDashboardWidget', { timeout: config.get('timeouts.waitFor'), }); }); it(`can view existing Dashboard`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('8fba09d8-df3f-5aa1-83cc-65f7fbcbc0d9'), - { + await PageObjects.dashboard.gotoDashboardURL({ + id: '8fba09d8-df3f-5aa1-83cc-65f7fbcbc0d9', + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: config.get('timeouts.waitFor'), }); @@ -125,41 +116,37 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`create new dashboard shows 404`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - DashboardConstants.CREATE_NEW_DASHBOARD_URL, - { + await PageObjects.dashboard.gotoDashboardURL({ + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await PageObjects.error.expectNotFound(); }); it(`edit dashboard for object which doesn't exist shows 404`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-dont-exist'), - { + await PageObjects.dashboard.gotoDashboardURL({ + id: 'i-dont-exist', + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await PageObjects.error.expectNotFound(); }); it(`edit dashboard for object which exists shows 404`, async () => { - await PageObjects.common.navigateToActualUrl( - 'dashboard', - createDashboardEditUrl('i-exist'), - { + await PageObjects.dashboard.gotoDashboardURL({ + id: 'i-exist', + args: { basePath: '/s/custom_space', ensureCurrentUrl: false, shouldLoginIfPrompted: false, - } - ); + }, + }); await PageObjects.error.expectNotFound(); }); }); From 670b6adb3ee48890d5d9d0bcff7ef48cd75c7571 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 28 Sep 2022 15:10:51 -0500 Subject: [PATCH 083/185] [DOCS] Adds time slider control (#141832) * [DOCS] Adds time slider control * Review comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dashboard_timeSliderControl_8.5.0.gif | Bin 0 -> 1196260 bytes ...imeSliderControl_advanceBackward_8.5.0.png | Bin 0 -> 1522 bytes ...timeSliderControl_advanceForward_8.5.0.png | Bin 0 -> 1531 bytes ...hboard_timeSliderControl_animate_8.5.0.png | Bin 0 -> 1559 bytes .../make-dashboards-interactive.asciidoc | 55 ++++++++++++++---- 5 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif create mode 100644 docs/user/dashboard/images/dashboard_timeSliderControl_advanceBackward_8.5.0.png create mode 100644 docs/user/dashboard/images/dashboard_timeSliderControl_advanceForward_8.5.0.png create mode 100644 docs/user/dashboard/images/dashboard_timeSliderControl_animate_8.5.0.png diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif b/docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif new file mode 100644 index 0000000000000000000000000000000000000000..89ca09dccc71e321a95088334240cef55405facc GIT binary patch literal 1196260 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

    =xb~|T2g=bESQ?7eYA-bL9bV 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

    -m*E_u&jRwH#Wmuys@8t;$?6IfuyCt&gy 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

    ;>Cli(9 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 z


    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%0D+S8GHjz{$m3pA5#63_48l-nu-4e>m{1C`j#Hw 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

    B#DCq5?XWNTzUz_8W7GNCy|r$~w5)z#W&%x? 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

    xFVa|J~k@_UEG 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

    &r7mW)N!*yWWO@NBkr%O#*QIKQzFOgS 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

    >usmlwilXfU8qx?jJo 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

    1W~L8JZr@bkjl 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

    dR_zaOM+vT$0o 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Î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 )Z<+d2 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

    A7;ffp}&zL!CA2|MYJ^c<1t9~fl4$%K&72A21X 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 pj%@b77=M> literal 0 HcmV?d00001 diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_advanceBackward_8.5.0.png b/docs/user/dashboard/images/dashboard_timeSliderControl_advanceBackward_8.5.0.png new file mode 100644 index 0000000000000000000000000000000000000000..e1c2e9921f687c1e5e29f950840787d05bc19696 GIT binary patch literal 1522 zcmeAS@N?(olHy`uVBq!ia0vp^d_XL~!3HGNrubPgFfhhvIy(n=Iy)-_6y>L7=A<$( zXiO}fVCdl#DAHzsn#1{oqSBIs3l&c&#RcXjDkYk9ghkx`oGP^Zz;pKrJ`i1k)OhB?HLzeYR1`rP&$zlH7p zT;V^LcCGik?3=fDPu{8VxuSjbG*h9;G08L*c|#``&eQsx^}pQge>JUHsFAeEtILG- zC11zl6B9)ECLd1V*uO#TvZ=}Md;8zt_;8IcVG-NfrESx1v|i4Au`|1(_V)$N?N*lI zyH?i6Uk@yP7VfsiYPb5rxA!*9`@L{4OTOyqD$VF?(UH%>7Y5a^3&^IP-6YIw0SxhJ zsS%!OzP=1vKsE;hE29ttGmymygba*Q46I-_1A`Z%G@Kp8r~y^O#K6#=$-n|t69uF} zzypYZx}h}lq&5b2AWsO0TNW_ERr4)ihO-5Latw~a&Yqq?Iyku~H8rm|BfsQ-_2zv* z0nP%C$YKTtZeb8+WSBKaf`Ng_Ix{4qB*NFnDmgz_FEJ%QDOIl`w*X`ggH44MkeQoW zlBiITo0C^;Rbi_HR$&EXgM{^!6u?SKvTc4x_5`cX3~Hd$?2OH9z_wsWAlwPj7+RcKhTlY&1l+5lI3`Bgg|*{zaKU%Mw8$Xb04TAqG`%qYnyRq~M0c zC0G;~nRZ+@`tXEd#}%W#DiN4W13X>$uqf80N32UD;2ssKf;q6rsV zIP?xYX1UYQcARn2l_zX%w_KPeG4EJl$db5T_x9;?O44F63jHaSWo7#+*NN@jQ&=(~ zj@?aZuH#}|<>xY?p^leLeqP*pJ-Apy!E#!vW^&~`_5YF64x8{ksa*FY;E;$b<7K0V zyY!xYX7@Rkye4e@4sIsN2~5{7HH7p%w(yuNa``38@56$9e0{7x`fk1nYj6!VJ1DfI zPUp0fh^vLQ<Hm&5}sw z+4uhOt7lTv#h-7;mHvK^X-@bL0Y=8Jb2IGY`KIPbv^B`s*xM@cIKSa#i_jB)@qPPK z*|SY9j3*roIC;CYP9Bl+5q$IAYKpB;fWDP)@J;2f=Z?(Evi>>Y599tb4eMTSy=Ao| gt-trW{nT?0)NLJ_YP){UnGDK^p00i_>zopr0F~Mp00000 literal 0 HcmV?d00001 diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_advanceForward_8.5.0.png b/docs/user/dashboard/images/dashboard_timeSliderControl_advanceForward_8.5.0.png new file mode 100644 index 0000000000000000000000000000000000000000..788621037cb073595c8324e9e8ede3f7b7f580a8 GIT binary patch literal 1531 zcmeAS@N?(olHy`uVBq!ia0vp^d_XM3!3HF=W8NDwFfhhvIy(n=Iy)-_6y>L7=A<$( zXiO}fVCdl#DAHzsn#1{oqSBIs3l&c&#RcXjDkYk9ghkx`oGP^Zz;pKrJ`i1k)OhB?HLzeYR1`rP&$zlH7p zT;V^LcCGik?3=fDPu{8VxuSjbG*h9;G08L*c|#``&eQsx^}pQge>JUHsFAeEtILG- zC11zl6B9)ECLd1V*uO#TvZ=}Md;8zt_;8IcVG-NfrESx1v|i4Au`|1(_V)$N?N*lI zyH?i6Uk@yP7VfsiYPb5rxA!*9`@L{4OTOyqD$VF?(UH%>7Y5a^3&^IP-6YIw0SxhJ zsS%!OzP=1vKsE;hE29ttGmymygba*Q46I-_1A`Z%G@Kp8r~y^O#K6#=$-n|t69uF} zzypYZx}h}lq&5b2AWsO0TNW_ERr4)ihO zv%n*=n1O*?7=#%aX3ddcU|_P&42dX-@b$4u&d=3LOvz75)vL%Y0GY#JQ(*;U=BAb; zDpcg=uf-VSp{TPq~=7rWaa{c8t61TV>27DEf^9AcS1CV7N-_D=jQ_T zmDnMo3aS}d5?v$M#t4LsMi$66B1s@=1bM*9zbF%ESt2L|?F?=7F~p$iZS+CGixk|D zxCDy=Bh!w{MjxIq?6|^?9s{Or#z;>W#}Es_vq8K44m$|Q%+J+P|H-mkQPk07$pU3w zA=8x_4PpYaLi-s`y0d8hVG@!xb>(mpdC1)%Fn8&VPq**H)r2Lf3wa&&{8pKto>u;C zjZfVhk!+o9Z0-U71Oo#_mSk$pOD*$SD>Nfa#Pxy+@2+*qo|A;k)s2ol_-*&@ckPaK z-ErHy+Yd5q%ROvzR`kdkF3tVDZXAah=07&fTerTTvaa#^LxG-^+)vglQsUdGdnGG$ z!hWxgJg`l#Kvu(5D^|4yt>rdy`*bC~!OjR#?&QwtSX<2Sr2m0SIDgVCcLg)1LR zUUe*aUiqV<-f{89&g09|=ggVcnN?IF^)hwS9rnX{tB4#IQCv>RpRtbFNFZ!JG({J%?uBuJa3!)Z+a-^rLpUXO@geCwJiWdg} literal 0 HcmV?d00001 diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_animate_8.5.0.png b/docs/user/dashboard/images/dashboard_timeSliderControl_animate_8.5.0.png new file mode 100644 index 0000000000000000000000000000000000000000..63a93323d6a36773a92907cd713e17bf9eef6621 GIT binary patch literal 1559 zcmeAS@N?(olHy`uVBq!ia0vp^d_XM3!3HF=W8NDwFfhhvIy(n=Iy)-_6y>L7=A<$( zXiO}fVCdl#DAHzsn#1{oqSBIs3l&c&#RcXjDkYk9ghkx`oGP^Zz;pKrJ`i1k)OhB?HLzeYR1`rP&$zlH7p zT;V^LcCGik?3=fDPu{8VxuSjbG*h9;G08L*c|#``&eQsx^}pQge>JUHsFAeEtILG- zC11zl6B9)ECLd1V*uO#TvZ=}Md;8zt_;8IcVG-NfrESx1v|i4Au`|1(_V)$N?N*lI zyH?i6Uk@yP7VfsiYPb5rxA!*9`@L{4OTOyqD$VF?(UH%>7Y5a^3&^IP-6YIw0SxhJ zsS%!OzP=1vKsE;hE29ttGmymygba*Q46I-_1A`Z%G@Kp8r~y^O#K6#=$-n|t69uF} zzypYZx}h}lq&5b2AWsO0TNW_ERr4)ihO zv%n*=n1O*?7=#%aX3ddcU|_P&42dX-@b$4u&d=3LOvz75)vL%Y0GY#JQ(*;U=BAb; zDpcg=uf-VSp{TPq~=7rWaa{c8t61TV>27DEf^9AcS1CV7N-_D=jQ_T zmDnMo3aS}d5?v$M#t4LsMi$66B1s@=1bM*9zbF%ESt2L|?F?=7F~p$iZS+CGixk|D zxCDy=Bh!w{MjxIq?6|^?9s{Or#!^og#}Es_wUbVJ9d;0D`=6QQvsbk1i&jU;QiUe% z*qt94kL=Y~Sm#pkK91!e`v*Z5c?SV;yT+!wC6|MA95sCNJzv#dO3{g0Bf{^K_Uz2h zf5p#yMEvHl%{U|!Y@qn5eu1OyYyQ+1Jz8Az9?yA{cC^Fxap;*Fy0TtHCd_>Et*;-> zs$x5Pkb$pO$@rG*+>IxA-tzN@|BjrZzpbz`z-8&h_RG8h4?z_X&(yqHQt@myXvz0K^2+cIi?aF)4zVHx#s$L4@33r(>tthANuGgb#+?5&!gA( zq<9uT6+WqD$}?-bugIl0#r6!7vo4?IJjxOIEWrC?+x*1KE3fx9;km{hMo@ymZ6b3GoJjk7HF5dsbbn550FmlyRql^wfBh3s1x5J>LDY zXVJ4FosB`=sZ!HAtyqs4ENo+YEEH8zzT@S|!+(>cdXj7OQhYYeKX2ICac()AJ!5ra Vj$){)-33r?^>p=fS?83{1ORqBF8%-j literal 0 HcmV?d00001 diff --git a/docs/user/dashboard/make-dashboards-interactive.asciidoc b/docs/user/dashboard/make-dashboards-interactive.asciidoc index 7c80fa858853..127c0a4a79e0 100644 --- a/docs/user/dashboard/make-dashboards-interactive.asciidoc +++ b/docs/user/dashboard/make-dashboards-interactive.asciidoc @@ -28,7 +28,7 @@ data-type="inline" *Controls* are interactive panels you add to your dashboards to filter and display only the data you want to explore. -There are two types of controls: +There are three types of controls: * *Options list* — Adds a dropdown that allows you to filter the data with one or more options that you select. + @@ -44,11 +44,17 @@ For example, if you are using the *[Logs] Web Traffic* dashboard from the sample [role="screenshot"] image::images/dashboard_controlsRangeSlider_8.3.0.png[Range slider control for the `hour_of_day` field with a range of `9` to `17` selected] +* *Time slider* — Adds a time range slider that allows you to filter the data within a specified range of time, advance the time range backward and forward, and animate your change in data over the specified time range. ++ +For example, you are using the *[Logs] Web Traffic* dashboard from the sample web logs data, and the global time filter is *Last 7 days*. When you add the time slider, you can click the previous and next buttons to advance the time range backward or forward, and click the play button to watch how the data changes over the last 7 days. +[role="screenshot"] +image::images/dashboard_timeSliderControl_8.5.0.gif[Time slider control for the the Last 7 days] + [float] -[[create-and-add-controls]] -==== Create and add controls +[[create-and-add-options-list-and-range-slider-controls]] +==== Create and add Options list and Range slider controls -To add interactive filters, create controls, then add them to your dashboard. +To add interactive Options list and Range slider controls, create the controls, then add them to your dashboard. . Open or create a new dashboard. @@ -79,8 +85,22 @@ The *Control type* is automatically applied for the field you selected. . Click *Save and close*. [float] -[[filter-the-data-with-options-lists]] -==== Filter the data with Options lists +[[add-time-slider-controls]] +==== Add time slider controls + +To add interactive time slider controls, create the control, then add it to your dashboard. + +. Open or create a new dashboard. + +. In the dashboard toolbar, click *Controls*, then select *Add time slider control*. + +. The time slider control uses the time range from the global time filter. To change the time range in the time slider control, <>. + +. Save the dashboard. + +[float] +[[filter-the-data-with-options-list-controls]] +==== Filter the data with Options list controls Filter the data with one or more options that you select. . Open the Options list dropdown. @@ -94,8 +114,8 @@ The dashboard displays only the data for the options you selected. . To display only the options you selected in the dropdown, click image:images/dashboard_showOnlySelectedOptions_8.3.0.png[The icon to display only the options you have selected in the Options list]. [float] -[[filter-the-data-with-range-sliders]] -==== Filter the data with Range sliders +[[filter-the-data-with-range-slider-controls]] +==== Filter the data with Range slider controls Filter the data within a specified range of values. . On the Range slider, click a value. @@ -106,6 +126,21 @@ The dashboard displays only the data for the range of values you specified. . To clear the specified values, click image:images/dashboard_controlsClearSelections_8.3.0.png[The icon to clear all specified values in the Range slider]. +[float] +[[filter-the-data-with-time-slider-controls]] +==== Filter the data with time slider controls +Filter the data within a specified range of time. + +. To view a different time range, click the time slider, then move the sliders to specify the time range you want to display. + +. To advance the time range forward, click image:images/dashboard_timeSliderControl_advanceForward_8.5.0.png[The icon to advance the time range forward]. + +. To advance the time range backward, click image:images/dashboard_timeSliderControl_advanceBackward_8.5.0.png[The icon to advance the time range backward]. + +. To animate the data changes over time, click image:images/dashboard_timeSliderControl_animate_8.5.0.png[The icon to clear all specified values in the Range slider]. + +. To clear the specified values, click image:images/dashboard_controlsClearSelections_8.3.0.png[The icon to clear all specified values in the Range slider]. + [float] [[configure-controls-settings]] ==== Configure the controls settings @@ -126,9 +161,9 @@ The dashboard displays only the data for the range of values you specified. [float] [[edit-controls]] -==== Edit controls +==== Edit Options list and Range slider control settings -Change the settings for a control. +Change the settings for the Options list and Range slider controls. . Hover over the control you want to edit, then click image:images/dashboard_controlsEditControl_8.3.0.png[The Edit control icon that opens the Edit control flyout]. From ccbac37155d6544a9f579e47052deb6a4eb69786 Mon Sep 17 00:00:00 2001 From: Tomasz Ciecierski Date: Wed, 28 Sep 2022 22:20:26 +0200 Subject: [PATCH 084/185] [Osquery] Fix small issues (#141083) --- .../osquery/cypress/e2e/all/alerts.cy.ts | 69 +-- .../all/cases.spec.ts => e2e/all/cases.cy.ts} | 4 +- x-pack/plugins/osquery/kibana.json | 1 + .../osquery/public/cases/add_to_cases.tsx | 27 + .../public/cases/add_to_cases_button.tsx | 4 +- .../public/discover/pack_view_in_discover.tsx | 49 ++ .../discover/view_results_in_discover.tsx | 141 +++++ .../public/form/results_type_field.tsx | 2 +- .../osquery/public/lens/pack_view_in_lens.tsx | 49 ++ .../public/lens/view_results_in_lens.tsx | 234 ++++++++ .../public/live_queries/form/index.tsx | 4 +- .../form/pack_queries_status_table.tsx | 521 ++---------------- .../live_queries/form/pack_results_header.tsx | 1 + .../osquery/public/packs/form/index.tsx | 2 +- .../public/packs/queries/query_flyout.tsx | 2 +- .../routes/live_queries/details/index.tsx | 29 +- .../public/routes/saved_queries/edit/tabs.tsx | 25 +- .../form/use_saved_query_form.tsx | 5 - .../osquery_action/index.tsx | 33 +- .../osquery_results/osquery_result.test.tsx | 32 +- .../osquery_results/osquery_result.tsx | 21 +- .../osquery_results/osquery_results.test.tsx | 32 +- .../osquery_results/test_utils.tsx | 38 ++ .../public/shared_components/prompts.tsx | 29 + .../server/routes/pack/create_pack_route.ts | 5 +- .../server/routes/pack/update_pack_route.ts | 5 +- .../osquery/server/routes/pack/utils.test.ts | 34 +- .../osquery/server/routes/pack/utils.ts | 21 +- .../saved_query/create_saved_query_route.ts | 4 +- .../markdown_editor/plugins/osquery/index.tsx | 26 +- .../plugins/osquery/not_available_prompt.tsx | 38 ++ .../markdown_editor/plugins/osquery/utils.ts | 31 -- .../test/osquery_cypress/artifact_manager.ts | 2 +- 33 files changed, 843 insertions(+), 677 deletions(-) rename x-pack/plugins/osquery/cypress/{integration/all/cases.spec.ts => e2e/all/cases.cy.ts} (96%) create mode 100644 x-pack/plugins/osquery/public/cases/add_to_cases.tsx create mode 100644 x-pack/plugins/osquery/public/discover/pack_view_in_discover.tsx create mode 100644 x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx create mode 100644 x-pack/plugins/osquery/public/lens/pack_view_in_lens.tsx create mode 100644 x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx create mode 100644 x-pack/plugins/osquery/public/shared_components/prompts.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/not_available_prompt.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts index e5c6b5013b13..34d04289ad67 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts @@ -8,7 +8,6 @@ import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; import { login } from '../../tasks/login'; import { - checkResults, findAndClickButton, findFormFieldByRowsLabelAndType, inputQuery, @@ -59,6 +58,28 @@ describe('Alert Event Details', () => { cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true'); }); + it('enables to add detection action with osquery', () => { + cy.visit('/app/security/rules'); + cy.contains(RULE_NAME).click(); + cy.contains('Edit rule settings').click(); + cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.contains('Perform no actions').get('select').select('On each rule execution'); + cy.contains('Response actions are run on each rule execution'); + cy.getBySel('.osquery-ResponseActionTypeSelectOption').click(); + cy.get(LIVE_QUERY_EDITOR); + cy.contains('Save changes').click(); + cy.contains('Query is a required field'); + inputQuery('select * from uptime'); + cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) + + // getSavedQueriesDropdown().type(`users{downArrow}{enter}`); + cy.contains('Save changes').click(); + cy.contains(`${RULE_NAME} was saved`).should('exist'); + cy.contains('Edit rule settings').click(); + cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.contains('select * from uptime'); + }); + it('should be able to run live query and add to timeline (-depending on the previous test)', () => { const TIMELINE_NAME = 'Untitled timeline'; cy.visit('/app/security/alerts'); @@ -94,38 +115,20 @@ describe('Alert Event Details', () => { cy.contains('Cancel').click(); cy.contains(TIMELINE_NAME).click(); cy.getBySel('draggableWrapperKeyboardHandler').contains('action_id: "'); - }); - it('enables to add detection action with osquery', () => { - cy.visit('/app/security/rules'); - cy.contains(RULE_NAME).click(); - cy.contains('Edit rule settings').click(); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); - cy.contains('Perform no actions').get('select').select('On each rule execution'); - cy.contains('Response actions are run on each rule execution'); - cy.getBySel('.osquery-ResponseActionTypeSelectOption').click(); - cy.get(LIVE_QUERY_EDITOR); - cy.contains('Save changes').click(); - cy.contains('Query is a required field'); - inputQuery('select * from uptime'); - cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) - - // getSavedQueriesDropdown().type(`users{downArrow}{enter}`); - cy.contains('Save changes').click(); - cy.contains(`${RULE_NAME} was saved`).should('exist'); - cy.contains('Edit rule settings').click(); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); - cy.contains('select * from uptime'); + // timeline unsaved changes modal + cy.visit('/app/osquery'); + closeModalIfVisible(); }); // TODO think on how to get these actions triggered faster (because now they are not triggered during the test). - it.skip('sees osquery results from last action', () => { - cy.visit('/app/security/alerts'); - cy.getBySel('header-page-title').contains('Alerts').should('exist'); - cy.getBySel('expand-event').first().click({ force: true }); - cy.contains('Osquery Results').click(); - cy.getBySel('osquery-results').should('exist'); - cy.contains('select * from uptime'); - cy.getBySel('osqueryResultsTable').within(() => { - checkResults(); - }); - }); + // it.skip('sees osquery results from last action', () => { + // cy.visit('/app/security/alerts'); + // cy.getBySel('header-page-title').contains('Alerts').should('exist'); + // cy.getBySel('expand-event').first().click({ force: true }); + // cy.contains('Osquery Results').click(); + // cy.getBySel('osquery-results').should('exist'); + // cy.contains('select * from uptime'); + // cy.getBySel('osqueryResultsTable').within(() => { + // checkResults(); + // }); + // }); }); diff --git a/x-pack/plugins/osquery/cypress/integration/all/cases.spec.ts b/x-pack/plugins/osquery/cypress/e2e/all/cases.cy.ts similarity index 96% rename from x-pack/plugins/osquery/cypress/integration/all/cases.spec.ts rename to x-pack/plugins/osquery/cypress/e2e/all/cases.cy.ts index cb16dd1dfb8f..ce586e4d4994 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/cases.spec.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/cases.cy.ts @@ -35,7 +35,7 @@ describe('Add to Cases', () => { cy.contains('Test Obs case').click(); checkResults(); cy.contains('attached Osquery results'); - cy.contains('SELECT * FROM users;'); + cy.contains('select * from uptime;'); cy.contains('View in Discover').should('exist'); cy.contains('View in Lens').should('exist'); cy.contains('Add to Case').should('not.exist'); @@ -66,7 +66,7 @@ describe('Add to Cases', () => { cy.contains('Test Security Case').click(); checkResults(); cy.contains('attached Osquery results'); - cy.contains('SELECT * FROM users;'); + cy.contains('select * from uptime;'); cy.contains('View in Discover').should('exist'); cy.contains('View in Lens').should('exist'); cy.contains('Add to Case').should('not.exist'); diff --git a/x-pack/plugins/osquery/kibana.json b/x-pack/plugins/osquery/kibana.json index 6d956fdce9fe..63e7718368ce 100644 --- a/x-pack/plugins/osquery/kibana.json +++ b/x-pack/plugins/osquery/kibana.json @@ -12,6 +12,7 @@ "requiredPlugins": [ "actions", "data", + "licensing", "dataViews", "discover", "features", diff --git a/x-pack/plugins/osquery/public/cases/add_to_cases.tsx b/x-pack/plugins/osquery/public/cases/add_to_cases.tsx new file mode 100644 index 000000000000..bc46a76edf36 --- /dev/null +++ b/x-pack/plugins/osquery/public/cases/add_to_cases.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useKibana } from '../common/lib/kibana'; +import type { AddToCaseButtonProps } from './add_to_cases_button'; +import { AddToCaseButton } from './add_to_cases_button'; + +const CASES_OWNER: string[] = []; + +export const AddToCaseWrapper: React.FC = React.memo((props) => { + const { cases } = useKibana().services; + const casePermissions = cases.helpers.canUseCases(); + const CasesContext = cases.ui.getCasesContext(); + + return ( + + {' '} + + ); +}); + +AddToCaseWrapper.displayName = 'AddToCaseWrapper'; diff --git a/x-pack/plugins/osquery/public/cases/add_to_cases_button.tsx b/x-pack/plugins/osquery/public/cases/add_to_cases_button.tsx index 9f2df87f3561..7124ebd95549 100644 --- a/x-pack/plugins/osquery/public/cases/add_to_cases_button.tsx +++ b/x-pack/plugins/osquery/public/cases/add_to_cases_button.tsx @@ -19,7 +19,7 @@ const ADD_TO_CASE = i18n.translate( } ); -interface IProps { +export interface AddToCaseButtonProps { queryId: string; agentIds?: string[]; actionId: string; @@ -28,7 +28,7 @@ interface IProps { iconProps?: Record; } -export const AddToCaseButton: React.FC = ({ +export const AddToCaseButton: React.FC = ({ actionId, agentIds = [], queryId = '', diff --git a/x-pack/plugins/osquery/public/discover/pack_view_in_discover.tsx b/x-pack/plugins/osquery/public/discover/pack_view_in_discover.tsx new file mode 100644 index 000000000000..c6506126c83f --- /dev/null +++ b/x-pack/plugins/osquery/public/discover/pack_view_in_discover.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment-timezone'; +import { usePackQueryLastResults } from '../packs/use_pack_query_last_results'; +import { ViewResultsActionButtonType } from '../live_queries/form/pack_queries_status_table'; +import { ViewResultsInDiscoverAction } from './view_results_in_discover'; + +interface PackViewInActionProps { + item: { + id: string; + interval: number; + action_id?: string; + agents: string[]; + }; + actionId?: string; +} +const PackViewInDiscoverActionComponent: React.FC = ({ item }) => { + const { action_id: actionId, agents: agentIds, interval } = item; + const { data: lastResultsData } = usePackQueryLastResults({ + actionId, + interval, + }); + + const startDate = lastResultsData?.['@timestamp'] + ? moment(lastResultsData?.['@timestamp'][0]).subtract(interval, 'seconds').toISOString() + : `now-${interval}s`; + const endDate = lastResultsData?.['@timestamp'] + ? moment(lastResultsData?.['@timestamp'][0]).toISOString() + : 'now'; + + return ( + + ); +}; + +export const PackViewInDiscoverAction = React.memo(PackViewInDiscoverActionComponent); diff --git a/x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx b/x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx new file mode 100644 index 000000000000..2106f11b8922 --- /dev/null +++ b/x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { useEffect, useState } from 'react'; +import { EuiButtonEmpty, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FilterStateStore } from '@kbn/es-query'; +import { useKibana } from '../common/lib/kibana'; +import { useLogsDataView } from '../common/hooks/use_logs_data_view'; +import { ViewResultsActionButtonType } from '../live_queries/form/pack_queries_status_table'; + +interface ViewResultsInDiscoverActionProps { + actionId?: string; + agentIds?: string[]; + buttonType: ViewResultsActionButtonType; + endDate?: string; + startDate?: string; + mode?: string; +} + +const ViewResultsInDiscoverActionComponent: React.FC = ({ + actionId, + agentIds, + buttonType, + endDate, + startDate, +}) => { + const { discover, application } = useKibana().services; + const locator = discover?.locator; + const discoverPermissions = application.capabilities.discover; + const { data: logsDataView } = useLogsDataView({ skip: !actionId }); + + const [discoverUrl, setDiscoverUrl] = useState(''); + + useEffect(() => { + const getDiscoverUrl = async () => { + if (!locator || !logsDataView) return; + + const agentIdsQuery = agentIds?.length + ? { + bool: { + minimum_should_match: 1, + should: agentIds.map((agentId) => ({ match_phrase: { 'agent.id': agentId } })), + }, + } + : null; + + const newUrl = await locator.getUrl({ + indexPatternId: logsDataView.id, + filters: [ + { + meta: { + index: logsDataView.id, + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'action_id', + params: { query: actionId }, + }, + query: { match_phrase: { action_id: actionId } }, + $state: { store: FilterStateStore.APP_STATE }, + }, + ...(agentIdsQuery + ? [ + { + $state: { store: FilterStateStore.APP_STATE }, + meta: { + alias: 'agent IDs', + disabled: false, + index: logsDataView.id, + key: 'query', + negate: false, + type: 'custom', + value: JSON.stringify(agentIdsQuery), + }, + query: agentIdsQuery, + }, + ] + : []), + ], + refreshInterval: { + pause: true, + value: 0, + }, + timeRange: + startDate && endDate + ? { + to: endDate, + from: startDate, + mode: 'absolute', + } + : { + to: 'now', + from: 'now-1d', + mode: 'relative', + }, + }); + setDiscoverUrl(newUrl); + }; + + getDiscoverUrl(); + }, [actionId, agentIds, endDate, startDate, locator, logsDataView]); + + if (!discoverPermissions.show) { + return null; + } + + if (buttonType === ViewResultsActionButtonType.button) { + return ( + + {VIEW_IN_DISCOVER} + + ); + } + + return ( + + + + ); +}; + +const VIEW_IN_DISCOVER = i18n.translate( + 'xpack.osquery.pack.queriesTable.viewDiscoverResultsActionAriaLabel', + { + defaultMessage: 'View in Discover', + } +); + +export const ViewResultsInDiscoverAction = React.memo(ViewResultsInDiscoverActionComponent); diff --git a/x-pack/plugins/osquery/public/form/results_type_field.tsx b/x-pack/plugins/osquery/public/form/results_type_field.tsx index 55bbe69397f9..ccc1961259c3 100644 --- a/x-pack/plugins/osquery/public/form/results_type_field.tsx +++ b/x-pack/plugins/osquery/public/form/results_type_field.tsx @@ -34,7 +34,7 @@ const DIFFERENTIAL_OPTION = { inputDisplay: ( ), }; diff --git a/x-pack/plugins/osquery/public/lens/pack_view_in_lens.tsx b/x-pack/plugins/osquery/public/lens/pack_view_in_lens.tsx new file mode 100644 index 000000000000..6e1cac3eda86 --- /dev/null +++ b/x-pack/plugins/osquery/public/lens/pack_view_in_lens.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment-timezone'; +import { usePackQueryLastResults } from '../packs/use_pack_query_last_results'; +import { ViewResultsActionButtonType } from '../live_queries/form/pack_queries_status_table'; +import { ViewResultsInLensAction } from './view_results_in_lens'; + +interface PackViewInActionProps { + item: { + id: string; + interval: number; + action_id?: string; + agents: string[]; + }; + actionId?: string; +} +const PackViewInLensActionComponent: React.FC = ({ item }) => { + const { action_id: actionId, agents: agentIds, interval } = item; + const { data: lastResultsData } = usePackQueryLastResults({ + actionId, + interval, + }); + + const startDate = lastResultsData?.['@timestamp'] + ? moment(lastResultsData?.['@timestamp'][0]).subtract(interval, 'seconds').toISOString() + : `now-${interval}s`; + const endDate = lastResultsData?.['@timestamp'] + ? moment(lastResultsData?.['@timestamp'][0]).toISOString() + : 'now'; + + return ( + + ); +}; + +export const PackViewInLensAction = React.memo(PackViewInLensActionComponent); diff --git a/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx b/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx new file mode 100644 index 000000000000..080c078f6a29 --- /dev/null +++ b/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import type { + PersistedIndexPatternLayer, + PieVisualizationState, + TermsIndexPatternColumn, + TypedLensByValueInput, +} from '@kbn/lens-plugin/public'; +import { DOCUMENT_FIELD_NAME as RECORDS_FIELD } from '@kbn/lens-plugin/common/constants'; +import { FilterStateStore } from '@kbn/es-query'; +import { ViewResultsActionButtonType } from '../live_queries/form/pack_queries_status_table'; +import type { LogsDataView } from '../common/hooks/use_logs_data_view'; +import { useKibana } from '../common/lib/kibana'; +import { useLogsDataView } from '../common/hooks/use_logs_data_view'; + +interface ViewResultsInLensActionProps { + actionId?: string; + agentIds?: string[]; + buttonType: ViewResultsActionButtonType; + endDate?: string; + startDate?: string; + mode?: string; +} + +const ViewResultsInLensActionComponent: React.FC = ({ + actionId, + agentIds, + buttonType, + endDate, + startDate, + mode, +}) => { + const lensService = useKibana().services.lens; + const { data: logsDataView } = useLogsDataView({ skip: !actionId }); + + const handleClick = useCallback( + (event) => { + event.preventDefault(); + + if (logsDataView) { + lensService?.navigateToPrefilledEditor( + { + id: '', + timeRange: { + from: startDate ?? 'now-1d', + to: endDate ?? 'now', + mode: mode ?? (startDate || endDate) ? 'absolute' : 'relative', + }, + attributes: getLensAttributes(logsDataView, actionId, agentIds), + }, + { + openInNewTab: true, + skipAppLeave: true, + } + ); + } + }, + [actionId, agentIds, endDate, lensService, logsDataView, mode, startDate] + ); + + const isDisabled = useMemo(() => !actionId || !logsDataView, [actionId, logsDataView]); + + if (buttonType === ViewResultsActionButtonType.button) { + return ( + + {VIEW_IN_LENS} + + ); + } + + return ( + + + + ); +}; + +function getLensAttributes( + logsDataView: LogsDataView, + actionId?: string, + agentIds?: string[] +): TypedLensByValueInput['attributes'] { + const dataLayer: PersistedIndexPatternLayer = { + columnOrder: ['8690befd-fd69-4246-af4a-dd485d2a3b38', 'ed999e9d-204c-465b-897f-fe1a125b39ed'], + columns: { + '8690befd-fd69-4246-af4a-dd485d2a3b38': { + sourceField: 'type', + isBucketed: true, + dataType: 'string', + scale: 'ordinal', + operationType: 'terms', + label: 'Top values of type', + params: { + otherBucket: true, + size: 5, + missingBucket: false, + orderBy: { + columnId: 'ed999e9d-204c-465b-897f-fe1a125b39ed', + type: 'column', + }, + orderDirection: 'desc', + }, + } as TermsIndexPatternColumn, + 'ed999e9d-204c-465b-897f-fe1a125b39ed': { + sourceField: RECORDS_FIELD, + isBucketed: false, + dataType: 'number', + scale: 'ratio', + operationType: 'count', + label: 'Count of records', + }, + }, + incompleteColumns: {}, + }; + + const xyConfig: PieVisualizationState = { + shape: 'pie', + layers: [ + { + layerType: 'data', + legendDisplay: 'default', + nestedLegend: false, + layerId: 'layer1', + metric: 'ed999e9d-204c-465b-897f-fe1a125b39ed', + numberDisplay: 'percent', + primaryGroups: ['8690befd-fd69-4246-af4a-dd485d2a3b38'], + categoryDisplay: 'default', + }, + ], + }; + + const agentIdsQuery = agentIds?.length + ? { + bool: { + minimum_should_match: 1, + should: agentIds?.map((agentId) => ({ match_phrase: { 'agent.id': agentId } })), + }, + } + : undefined; + + return { + visualizationType: 'lnsPie', + title: `Action ${actionId} results`, + references: [ + { + id: logsDataView.id, + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: logsDataView.id, + name: 'indexpattern-datasource-layer-layer1', + type: 'index-pattern', + }, + { + name: 'filter-index-pattern-0', + id: logsDataView.id, + type: 'index-pattern', + }, + ], + state: { + datasourceStates: { + indexpattern: { + layers: { + layer1: dataLayer, + }, + }, + }, + filters: [ + { + $state: { store: FilterStateStore.APP_STATE }, + meta: { + index: 'filter-index-pattern-0', + negate: false, + alias: null, + disabled: false, + params: { + query: actionId, + }, + type: 'phrase', + key: 'action_id', + }, + query: { + match_phrase: { + action_id: actionId, + }, + }, + }, + ...(agentIdsQuery + ? [ + { + $state: { store: FilterStateStore.APP_STATE }, + meta: { + alias: 'agent IDs', + disabled: false, + index: 'filter-index-pattern-0', + key: 'query', + negate: false, + type: 'custom', + value: JSON.stringify(agentIdsQuery), + }, + query: agentIdsQuery, + }, + ] + : []), + ], + query: { language: 'kuery', query: '' }, + visualization: xyConfig, + }, + }; +} + +const VIEW_IN_LENS = i18n.translate( + 'xpack.osquery.pack.queriesTable.viewLensResultsActionAriaLabel', + { + defaultMessage: 'View in Lens', + } +); + +export const ViewResultsInLensAction = React.memo(ViewResultsInLensActionComponent); diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index 4a6a204cec0b..b870d1385752 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -12,6 +12,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useForm as useHookForm, FormProvider } from 'react-hook-form'; import { isEmpty, find, pickBy } from 'lodash'; +import { AddToCaseWrapper } from '../../cases/add_to_cases'; import type { AddToTimelinePayload } from '../../timelines/get_add_to_timeline'; import { QueryPackSelectable } from './query_pack_selectable'; import type { SavedQuerySOFormData } from '../../saved_queries/form/use_saved_query_form'; @@ -25,7 +26,6 @@ import type { AgentSelection } from '../../agents/types'; import { LiveQueryQueryField } from './live_query_query_field'; import { AgentsTableField } from './agents_table_field'; import { savedQueryDataSerializer } from '../../saved_queries/form/use_saved_query_form'; -import { AddToCaseButton } from '../../cases/add_to_cases_button'; import { PackFieldWrapper } from '../../shared_components/osquery_response_action_type/pack_field_wrapper'; export interface LiveQueryFormFields { @@ -213,7 +213,7 @@ const LiveQueryFormComponent: React.FC = ({ (payload) => { if (liveQueryActionId) { return ( - ({ match_phrase: { 'agent.id': agentId } })), - }, - } - : undefined; - - return { - visualizationType: 'lnsPie', - title: `Action ${actionId} results`, - references: [ - { - id: logsDataView.id, - name: 'indexpattern-datasource-current-indexpattern', - type: 'index-pattern', - }, - { - id: logsDataView.id, - name: 'indexpattern-datasource-layer-layer1', - type: 'index-pattern', - }, - { - name: 'filter-index-pattern-0', - id: logsDataView.id, - type: 'index-pattern', - }, - ], - state: { - datasourceStates: { - indexpattern: { - layers: { - layer1: dataLayer, - }, - }, - }, - filters: [ - { - $state: { store: FilterStateStore.APP_STATE }, - meta: { - index: 'filter-index-pattern-0', - negate: false, - alias: null, - disabled: false, - params: { - query: actionId, - }, - type: 'phrase', - key: 'action_id', - }, - query: { - match_phrase: { - action_id: actionId, - }, - }, - }, - ...(agentIdsQuery - ? [ - { - $state: { store: FilterStateStore.APP_STATE }, - meta: { - alias: 'agent IDs', - disabled: false, - index: 'filter-index-pattern-0', - key: 'query', - negate: false, - type: 'custom', - value: JSON.stringify(agentIdsQuery), - }, - query: agentIdsQuery, - }, - ] - : []), - ], - query: { language: 'kuery', query: '' }, - visualization: xyConfig, - }, - }; -} - -const ViewResultsInLensActionComponent: React.FC = ({ - actionId, - agentIds, - buttonType, - endDate, - startDate, - mode, -}) => { - const lensService = useKibana().services.lens; - const isLensAvailable = lensService?.canUseEditor(); - const { data: logsDataView } = useLogsDataView({ skip: !actionId }); - - const handleClick = useCallback( - (event) => { - event.preventDefault(); - - if (logsDataView) { - lensService?.navigateToPrefilledEditor( - { - id: '', - timeRange: { - from: startDate ?? 'now-1d', - to: endDate ?? 'now', - mode: mode ?? (startDate || endDate) ? 'absolute' : 'relative', - }, - attributes: getLensAttributes(logsDataView, actionId, agentIds), - }, - { - openInNewTab: true, - skipAppLeave: true, - } - ); - } - }, - [actionId, agentIds, endDate, lensService, logsDataView, mode, startDate] - ); - - const isDisabled = useMemo(() => !actionId || !logsDataView, [actionId, logsDataView]); - - if (!isLensAvailable) { - return null; - } - - if (buttonType === ViewResultsActionButtonType.button) { - return ( - - {VIEW_IN_LENS} - - ); - } - - return ( - - - - ); -}; - -export const ViewResultsInLensAction = React.memo(ViewResultsInLensActionComponent); - -const ViewResultsInDiscoverActionComponent: React.FC = ({ - actionId, - agentIds, - buttonType, - endDate, - startDate, -}) => { - const { discover, application } = useKibana().services; - const locator = discover?.locator; - const discoverPermissions = application.capabilities.discover; - const { data: logsDataView } = useLogsDataView({ skip: !actionId }); - - const [discoverUrl, setDiscoverUrl] = useState(''); - - useEffect(() => { - const getDiscoverUrl = async () => { - if (!locator || !logsDataView) return; - - const agentIdsQuery = agentIds?.length - ? { - bool: { - minimum_should_match: 1, - should: agentIds.map((agentId) => ({ match_phrase: { 'agent.id': agentId } })), - }, - } - : null; - - const newUrl = await locator.getUrl({ - indexPatternId: logsDataView.id, - filters: [ - { - meta: { - index: logsDataView.id, - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: 'action_id', - params: { query: actionId }, - }, - query: { match_phrase: { action_id: actionId } }, - $state: { store: FilterStateStore.APP_STATE }, - }, - ...(agentIdsQuery - ? [ - { - $state: { store: FilterStateStore.APP_STATE }, - meta: { - alias: 'agent IDs', - disabled: false, - index: logsDataView.id, - key: 'query', - negate: false, - type: 'custom', - value: JSON.stringify(agentIdsQuery), - }, - query: agentIdsQuery, - }, - ] - : []), - ], - refreshInterval: { - pause: true, - value: 0, - }, - timeRange: - startDate && endDate - ? { - to: endDate, - from: startDate, - mode: 'absolute', - } - : { - to: 'now', - from: 'now-1d', - mode: 'relative', - }, - }); - setDiscoverUrl(newUrl); - }; - - getDiscoverUrl(); - }, [actionId, agentIds, endDate, startDate, locator, logsDataView]); - - if (!discoverPermissions.show) { - return null; - } - - if (buttonType === ViewResultsActionButtonType.button) { - return ( - - {VIEW_IN_DISCOVER} - - ); - } - - return ( - - - - ); -}; - -export const ViewResultsInDiscoverAction = React.memo(ViewResultsInDiscoverActionComponent); - interface DocsColumnResultsProps { count?: number; isLive?: boolean; @@ -450,72 +99,6 @@ const AgentsColumnResults: React.FC = ({ ); -interface PackViewInActionProps { - item: { - id: string; - interval: number; - action_id?: string; - agents: string[]; - }; - actionId?: string; -} - -const PackViewInDiscoverActionComponent: React.FC = ({ item }) => { - const { action_id: actionId, agents: agentIds, interval } = item; - const { data: lastResultsData } = usePackQueryLastResults({ - actionId, - interval, - }); - - const startDate = lastResultsData?.['@timestamp'] - ? moment(lastResultsData?.['@timestamp'][0]).subtract(interval, 'seconds').toISOString() - : `now-${interval}s`; - const endDate = lastResultsData?.['@timestamp'] - ? moment(lastResultsData?.['@timestamp'][0]).toISOString() - : 'now'; - - return ( - - ); -}; - -const PackViewInDiscoverAction = React.memo(PackViewInDiscoverActionComponent); - -const PackViewInLensActionComponent: React.FC = ({ item }) => { - const { action_id: actionId, agents: agentIds, interval } = item; - const { data: lastResultsData } = usePackQueryLastResults({ - actionId, - interval, - }); - - const startDate = lastResultsData?.['@timestamp'] - ? moment(lastResultsData?.['@timestamp'][0]).subtract(interval, 'seconds').toISOString() - : `now-${interval}s`; - const endDate = lastResultsData?.['@timestamp'] - ? moment(lastResultsData?.['@timestamp'][0]).toISOString() - : 'now'; - - return ( - - ); -}; - -const PackViewInLensAction = React.memo(PackViewInLensActionComponent); - type PackQueryStatusItem = Partial<{ action_id: string; id: string; @@ -542,10 +125,12 @@ interface PackQueriesStatusTableProps { actionId, isIcon, isDisabled, + queryId, }: { actionId?: string; isIcon?: boolean; isDisabled?: boolean; + queryId?: string; }) => ReactElement; showResultsHeader?: boolean; } @@ -562,10 +147,6 @@ const PackQueriesStatusTableComponent: React.FC = ( showResultsHeader, }) => { const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>({}); - const { cases, timelines, appName } = useKibana().services; - const casePermissions = cases.helpers.canUseCases(); - const CasesContext = cases.ui.getCasesContext(); - const renderIDColumn = useCallback( (id: string) => ( @@ -619,11 +200,15 @@ const PackQueriesStatusTableComponent: React.FC = ( const renderLensResultsAction = useCallback((item) => , []); const handleAddToCase = useCallback( - (payload: { actionId: string; isIcon?: boolean }) => + (payload: { actionId?: string; isIcon?: boolean; queryId: string }) => // eslint-disable-next-line react/display-name () => { if (addToCase) { - return addToCase({ actionId: payload.actionId, isIcon: payload?.isIcon }); + return addToCase({ + actionId: payload.actionId, + isIcon: payload.isIcon, + queryId: payload.queryId, + }); } return <>; @@ -648,7 +233,7 @@ const PackQueriesStatusTableComponent: React.FC = ( agentIds={agentIds} failedAgentsCount={item?.failed ?? 0} addToTimeline={addToTimeline} - addToCase={addToCase && handleAddToCase({ actionId: item.action_id })} + addToCase={addToCase && handleAddToCase({ queryId: item.action_id, actionId })} /> @@ -658,7 +243,7 @@ const PackQueriesStatusTableComponent: React.FC = ( return itemIdToExpandedRowMapValues; }); }, - [startDate, expirationDate, agentIds, addToTimeline, addToCase, handleAddToCase] + [startDate, expirationDate, agentIds, addToTimeline, addToCase, handleAddToCase, actionId] ); const renderToggleResultsAction = useCallback( @@ -677,28 +262,37 @@ const PackQueriesStatusTableComponent: React.FC = ( const getItemId = useCallback((item: PackItem) => get(item, 'id'), []); - const columns = useMemo(() => { - const resultActions = [ - { - render: renderDiscoverResultsAction, - }, - { - render: renderLensResultsAction, - }, - { - available: () => !!addToCase, - render: (item: { action_id: string }) => - addToCase && - addToCase({ actionId: item.action_id, isIcon: true, isDisabled: !item.action_id }), - }, - { - available: () => addToTimeline && timelines && appName === SECURITY_APP_NAME, - render: (item: { action_id: string }) => - addToTimeline && addToTimeline({ query: ['action_id', item.action_id], isIcon: true }), - }, - ]; + const renderResultActions = useCallback( + (row: { action_id: string }) => { + const resultActions = [ + { + render: renderDiscoverResultsAction, + }, + { + render: renderLensResultsAction, + }, + { + render: (item: { action_id: string }) => + addToTimeline && addToTimeline({ query: ['action_id', item.action_id], isIcon: true }), + }, + { + render: (item: { action_id: string }) => + addToCase && + addToCase({ + actionId, + queryId: item.action_id, + isIcon: true, + isDisabled: !item.action_id, + }), + }, + ]; - return [ + return resultActions.map((action) => action.render(row)); + }, + [actionId, addToCase, addToTimeline, renderDiscoverResultsAction, renderLensResultsAction] + ); + const columns = useMemo( + () => [ { field: 'id', name: i18n.translate('xpack.osquery.pack.queriesTable.idColumnTitle', { @@ -734,7 +328,7 @@ const PackQueriesStatusTableComponent: React.FC = ( defaultMessage: 'View results', }), width: '90px', - actions: resultActions, + render: renderResultActions, }, { id: 'actions', @@ -747,21 +341,16 @@ const PackQueriesStatusTableComponent: React.FC = ( }, ], }, - ]; - }, [ - renderDiscoverResultsAction, - renderLensResultsAction, - renderIDColumn, - renderQueryColumn, - renderDocsColumn, - renderAgentsColumn, - renderToggleResultsAction, - addToCase, - addToTimeline, - timelines, - appName, - ]); - + ], + [ + renderIDColumn, + renderQueryColumn, + renderDocsColumn, + renderAgentsColumn, + renderResultActions, + renderToggleResultsAction, + ] + ); const sorting = useMemo( () => ({ sort: { @@ -798,7 +387,7 @@ const PackQueriesStatusTableComponent: React.FC = ( ); return ( - + <> {showResultsHeader && ( )} @@ -811,7 +400,7 @@ const PackQueriesStatusTableComponent: React.FC = ( itemIdToExpandedRowMap={itemIdToExpandedRowMap} isExpandable /> - + ); }; diff --git a/x-pack/plugins/osquery/public/live_queries/form/pack_results_header.tsx b/x-pack/plugins/osquery/public/live_queries/form/pack_results_header.tsx index 854548fdb58e..b4892a0387e8 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/pack_results_header.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/pack_results_header.tsx @@ -35,6 +35,7 @@ const StyledIconsList = styled(EuiFlexItem)` export const PackResultsHeader = ({ actionId, addToCase }: PackResultsHeadersProps) => ( <> + diff --git a/x-pack/plugins/osquery/public/packs/form/index.tsx b/x-pack/plugins/osquery/public/packs/form/index.tsx index 594c539c389c..ac3892fe9748 100644 --- a/x-pack/plugins/osquery/public/packs/form/index.tsx +++ b/x-pack/plugins/osquery/public/packs/form/index.tsx @@ -155,7 +155,7 @@ const PackFormComponent: React.FC = ({ - + diff --git a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx index d709b4f639e6..65d829e7b7e8 100644 --- a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx @@ -79,7 +79,7 @@ const QueryFlyoutComponent: React.FC = ({ resetField('version', { defaultValue: savedQuery.version ? [savedQuery.version] : [] }); resetField('interval', { defaultValue: savedQuery.interval ? savedQuery.interval : 3600 }); resetField('snapshot', { defaultValue: savedQuery.snapshot ?? true }); - resetField('removed'); + resetField('removed', { defaultValue: savedQuery.removed }); resetField('ecs_mapping', { defaultValue: savedQuery.ecs_mapping ?? {} }); } }, diff --git a/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx index a4c58074c76e..bf35a529137f 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx @@ -10,13 +10,18 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { AddToCaseButton } from '../../../cases/add_to_cases_button'; +import styled from 'styled-components'; +import { AddToCaseWrapper } from '../../../cases/add_to_cases'; import { useRouterNavigate } from '../../../common/lib/kibana'; import { WithHeaderLayout } from '../../../components/layouts'; import { useLiveQueryDetails } from '../../../actions/use_live_query_details'; import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs'; import { PackQueriesStatusTable } from '../../../live_queries/form/pack_queries_status_table'; +const StyledTableWrapper = styled(EuiFlexItem)` + padding-left: 10px; +`; + const LiveQueryDetailsPageComponent = () => { const { actionId } = useParams<{ actionId: string }>(); useBreadcrumbs('live_query_details', { liveQueryId: actionId }); @@ -55,7 +60,7 @@ const LiveQueryDetailsPageComponent = () => { }, [data?.status]); const addToCaseButton = useCallback( (payload) => ( - { return ( - + + + ); }; diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx index 49501bc24b70..f4de8b04cb47 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx @@ -10,13 +10,10 @@ import React, { useMemo } from 'react'; import type { ReactElement } from 'react'; import type { ECSMapping } from '@kbn/osquery-io-ts-types'; -import { useKibana } from '../../../common/lib/kibana'; import type { AddToTimelinePayload } from '../../../timelines/get_add_to_timeline'; import { ResultsTable } from '../../../results/results_table'; import { ActionResultsSummary } from '../../../action_results/action_results_summary'; -const CASES_OWNER: string[] = []; - interface ResultTabsProps { actionId: string; agentIds?: string[]; @@ -38,10 +35,6 @@ const ResultTabsComponent: React.FC = ({ addToTimeline, addToCase, }) => { - const { cases } = useKibana().services; - const casePermissions = cases.helpers.canUseCases(); - const CasesContext = cases.ui.getCasesContext(); - const tabs = useMemo( () => [ { @@ -85,16 +78,14 @@ const ResultTabsComponent: React.FC = ({ ); return ( - - - + ); }; diff --git a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx index 3392f2af037c..76bdd77d17b1 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx @@ -72,11 +72,6 @@ export const savedQueryDataSerializer = (payload: SavedQueryFormData): SavedQuer draft.interval = draft.interval + ''; } - if (draft.snapshot) { - delete draft.snapshot; - delete draft.removed; - } - return draft; }); diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx index 6bb8d6f5a563..3095428d478d 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx @@ -6,17 +6,12 @@ */ import { EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { OsqueryEmptyPrompt, OsqueryNotAvailablePrompt } from '../prompts'; import type { AddToTimelinePayload } from '../../timelines/get_add_to_timeline'; -import { - AGENT_STATUS_ERROR, - EMPTY_PROMPT, - NOT_AVAILABLE, - PERMISSION_DENIED, - SHORT_EMPTY_TITLE, -} from './translations'; +import { AGENT_STATUS_ERROR, PERMISSION_DENIED, SHORT_EMPTY_TITLE } from './translations'; import { useKibana } from '../../common/lib/kibana'; import { LiveQuery } from '../../live_queries'; import { OsqueryIcon } from '../../components/osquery_icon'; @@ -39,22 +34,11 @@ const OsqueryActionComponent: React.FC = ({ }) => { const permissions = useKibana().services.application.capabilities.osquery; - const emptyPrompt = useMemo( - () => ( - } - title={

    {SHORT_EMPTY_TITLE}

    } - titleSize="xs" - body={

    {EMPTY_PROMPT}

    } - /> - ), - [] - ); const { osqueryAvailable, agentFetched, isLoading, policyFetched, policyLoading, agentData } = useIsOsqueryAvailable(agentId); if (agentId && agentFetched && !agentData) { - return emptyPrompt; + return ; } if ( @@ -91,14 +75,7 @@ const OsqueryActionComponent: React.FC = ({ } if (agentId && !osqueryAvailable) { - return ( - } - title={

    {SHORT_EMPTY_TITLE}

    } - titleSize="xs" - body={

    {NOT_AVAILABLE}

    } - /> - ); + return ; } if (agentId && agentData?.status !== 'online') { diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.test.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.test.tsx index e33207a12d90..772df753c897 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.test.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.test.tsx @@ -20,7 +20,7 @@ import { DETAILS_ID, DETAILS_QUERY, DETAILS_TIMESTAMP, - mockCasesContext, + getMockedKibanaConfig, } from './test_utils'; jest.mock('../../common/lib/kibana'); @@ -43,34 +43,8 @@ const defaultProps = { queryId: '', }; const mockKibana = (permissionType: unknown = defaultPermissions) => { - useKibanaMock.mockReturnValue({ - services: { - application: { - capabilities: permissionType, - }, - cases: { - helpers: { - canUseCases: jest.fn(), - }, - ui: { - getCasesContext: jest.fn().mockImplementation(() => mockCasesContext), - }, - }, - data: { - dataViews: { - getCanSaveSync: jest.fn(), - hasData: { - hasESData: jest.fn(), - hasUserDataView: jest.fn(), - hasDataView: jest.fn(), - }, - }, - }, - notifications: { - toasts: jest.fn(), - }, - }, - } as unknown as ReturnType); + const mockedKibana = getMockedKibanaConfig(permissionType); + useKibanaMock.mockReturnValue(mockedKibana); }; const renderWithContext = (Element: React.ReactElement) => diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.tsx index 998de8a15cfc..b0d0691c14f7 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result.tsx @@ -6,9 +6,10 @@ */ import { EuiComment, EuiSpacer } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedRelative } from '@kbn/i18n-react'; +import { AddToCaseWrapper } from '../../cases/add_to_cases'; import type { OsqueryActionResultsProps } from './types'; import { useLiveQueryDetails } from '../../actions/use_live_query_details'; import { ATTACHED_QUERY } from '../../agents/translations'; @@ -22,7 +23,6 @@ interface OsqueryResultProps extends Omit export const OsqueryResult = ({ actionId, - queryId, ruleName, addToTimeline, agentIds, @@ -30,10 +30,22 @@ export const OsqueryResult = ({ }: OsqueryResultProps) => { const { data } = useLiveQueryDetails({ actionId, - // isLive, - // ...(queryId ? { queryIds: [queryId] } : {}), }); + const addToCaseButton = useCallback( + (payload) => ( + + ), + [data?.agents, actionId] + ); + return (
    @@ -51,6 +63,7 @@ export const OsqueryResult = ({ expirationDate={data?.expiration} agentIds={agentIds} addToTimeline={addToTimeline} + addToCase={addToCaseButton} /> diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.test.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.test.tsx index 9e2b74e00070..5190d10dc1c7 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.test.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.test.tsx @@ -17,7 +17,7 @@ import * as useAllLiveQueries from '../../actions/use_all_live_queries'; import * as useLiveQueryDetails from '../../actions/use_live_query_details'; import { PERMISSION_DENIED } from '../osquery_action/translations'; import * as privileges from '../../action_results/use_action_privileges'; -import { defaultLiveQueryDetails, DETAILS_QUERY, mockCasesContext } from './test_utils'; +import { defaultLiveQueryDetails, DETAILS_QUERY, getMockedKibanaConfig } from './test_utils'; jest.mock('../../common/lib/kibana'); @@ -49,34 +49,8 @@ const defaultPermissions = { }; const mockKibana = (permissionType: unknown = defaultPermissions) => { - useKibanaMock.mockReturnValue({ - services: { - application: { - capabilities: permissionType, - }, - cases: { - helpers: { - canUseCases: jest.fn(), - }, - ui: { - getCasesContext: jest.fn().mockImplementation(() => mockCasesContext), - }, - }, - data: { - dataViews: { - getCanSaveSync: jest.fn(), - hasData: { - hasESData: jest.fn(), - hasUserDataView: jest.fn(), - hasDataView: jest.fn(), - }, - }, - }, - notifications: { - toasts: jest.fn(), - }, - }, - } as unknown as ReturnType); + const mockedKibana = getMockedKibanaConfig(permissionType); + useKibanaMock.mockReturnValue(mockedKibana); }; const renderWithContext = (Element: React.ReactElement) => diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_results/test_utils.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_results/test_utils.tsx index 4de58a4ed9aa..c1c991becc8a 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_results/test_utils.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_results/test_utils.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import type { useKibana } from '../../common/lib/kibana'; export const DETAILS_QUERY = 'select * from uptime'; export const DETAILS_ID = 'test-id'; @@ -38,4 +39,41 @@ export const defaultLiveQueryDetails = { }, } as never; +export const getMockedKibanaConfig = (permissionType: unknown) => + ({ + services: { + application: { + capabilities: permissionType, + }, + cases: { + helpers: { + canUseCases: jest.fn().mockImplementation(() => ({ + read: true, + update: true, + push: true, + })), + }, + ui: { + getCasesContext: jest.fn().mockImplementation(() => mockCasesContext), + }, + hooks: { + getUseCasesAddToExistingCaseModal: jest.fn(), + }, + }, + data: { + dataViews: { + getCanSaveSync: jest.fn(), + hasData: { + hasESData: jest.fn(), + hasUserDataView: jest.fn(), + hasDataView: jest.fn(), + }, + }, + }, + notifications: { + toasts: jest.fn(), + }, + }, + } as unknown as ReturnType); + export const mockCasesContext: React.FC = (props) => <>{props?.children ?? null}; diff --git a/x-pack/plugins/osquery/public/shared_components/prompts.tsx b/x-pack/plugins/osquery/public/shared_components/prompts.tsx new file mode 100644 index 000000000000..992de7c9ab14 --- /dev/null +++ b/x-pack/plugins/osquery/public/shared_components/prompts.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { OsqueryIcon } from '../components/osquery_icon'; +import { EMPTY_PROMPT, NOT_AVAILABLE, SHORT_EMPTY_TITLE } from './osquery_action/translations'; + +export const OsqueryEmptyPrompt = () => ( + } + title={

    {SHORT_EMPTY_TITLE}

    } + titleSize="xs" + body={

    {EMPTY_PROMPT}

    } + /> +); + +export const OsqueryNotAvailablePrompt = () => ( + } + title={

    {SHORT_EMPTY_TITLE}

    } + titleSize="xs" + body={

    {NOT_AVAILABLE}

    } + /> +); diff --git a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts index c60c6d476974..3dda3cdb36b0 100644 --- a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts @@ -144,7 +144,10 @@ export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppConte } set(draft, `inputs[0].config.osquery.value.packs.${packSO.attributes.name}`, { - queries: convertSOQueriesToPack(queries, { removeMultiLines: true }), + queries: convertSOQueriesToPack(queries, { + removeMultiLines: true, + removeResultType: true, + }), }); return draft; diff --git a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts index c113cbeb36c3..5b0d76131ee2 100644 --- a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts @@ -285,6 +285,7 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte { queries: convertSOQueriesToPack(updatedPackSO.attributes.queries, { removeMultiLines: true, + removeResultType: true, }), } ); @@ -315,7 +316,9 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte draft, `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, { - queries: updatedPackSO.attributes.queries, + queries: convertSOQueriesToPack(updatedPackSO.attributes.queries, { + removeResultType: true, + }), } ); diff --git a/x-pack/plugins/osquery/server/routes/pack/utils.test.ts b/x-pack/plugins/osquery/server/routes/pack/utils.test.ts index 05aff2807393..3ddb074a6edb 100644 --- a/x-pack/plugins/osquery/server/routes/pack/utils.test.ts +++ b/x-pack/plugins/osquery/server/routes/pack/utils.test.ts @@ -42,15 +42,45 @@ describe('Pack utils', () => { describe('convertSOQueriesToPack', () => { test('converts to pack with empty ecs_mapping', () => { const convertedQueries = convertSOQueriesToPack(getTestQueries()); - expect(convertedQueries).toStrictEqual(getTestQueries({})); + expect(convertedQueries).toStrictEqual(getTestQueries()); }); test('converts to pack with converting query to single line', () => { const convertedQueries = convertSOQueriesToPack(getTestQueries(), { removeMultiLines: true }); - expect(convertedQueries).toStrictEqual(oneLiner); + expect(convertedQueries).toStrictEqual({ + ...oneLiner, + }); }); test('converts to object with pack names after query.id', () => { const convertedQueries = convertSOQueriesToPack(getTestQueries({ id: 'testId' })); expect(convertedQueries).toStrictEqual(getTestQueries({}, 'testId')); }); + test('converts with results snapshot set false', () => { + const convertedQueries = convertSOQueriesToPack( + getTestQueries({ snapshot: false, removed: true }), + { removeResultType: true } + ); + expect(convertedQueries).toStrictEqual(getTestQueries({ removed: true, snapshot: false })); + }); + test('converts with results snapshot set true and removed false', () => { + const convertedQueries = convertSOQueriesToPack( + getTestQueries({ snapshot: true, removed: true }), + { removeResultType: true } + ); + expect(convertedQueries).toStrictEqual(getTestQueries({})); + }); + test('converts with results snapshot set true but removed false', () => { + const convertedQueries = convertSOQueriesToPack( + getTestQueries({ snapshot: true, removed: false }), + { removeResultType: true } + ); + expect(convertedQueries).toStrictEqual(getTestQueries({})); + }); + test('converts with both results set to false', () => { + const convertedQueries = convertSOQueriesToPack( + getTestQueries({ snapshot: false, removed: false }), + { removeResultType: true } + ); + expect(convertedQueries).toStrictEqual(getTestQueries({ removed: false, snapshot: false })); + }); }); }); diff --git a/x-pack/plugins/osquery/server/routes/pack/utils.ts b/x-pack/plugins/osquery/server/routes/pack/utils.ts index fe1ded84f446..4342cdb3ead8 100644 --- a/x-pack/plugins/osquery/server/routes/pack/utils.ts +++ b/x-pack/plugins/osquery/server/routes/pack/utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty, pick, reduce } from 'lodash'; +import { isEmpty, pick, reduce, isArray } from 'lodash'; import { DEFAULT_PLATFORM } from '../../../common/constants'; import { removeMultilines } from '../../../common/utils/build_query/remove_multilines'; import { convertECSMappingToArray, convertECSMappingToObject } from '../utils'; @@ -37,18 +37,29 @@ export const convertPackQueriesToSO = (queries) => }> ); -// @ts-expect-error update types -export const convertSOQueriesToPack = (queries, options?: { removeMultiLines?: boolean }) => +export const convertSOQueriesToPack = ( + // @ts-expect-error update types + queries, + options?: { removeMultiLines?: boolean; removeResultType?: boolean } +) => reduce( queries, // eslint-disable-next-line @typescript-eslint/naming-convention - (acc, { id: queryId, ecs_mapping, query, platform, ...rest }, key) => { + (acc, { id: queryId, ecs_mapping, query, platform, removed, snapshot, ...rest }, key) => { + const resultType = !snapshot ? { removed, snapshot } : {}; const index = queryId ? queryId : key; acc[index] = { ...rest, query: options?.removeMultiLines ? removeMultilines(query) : query, - ...(!isEmpty(ecs_mapping) ? { ecs_mapping: convertECSMappingToObject(ecs_mapping) } : {}), + ...(!isEmpty(ecs_mapping) + ? isArray(ecs_mapping) + ? { ecs_mapping: convertECSMappingToObject(ecs_mapping) } + : { ecs_mapping } + : {}), ...(platform === DEFAULT_PLATFORM || platform === undefined ? {} : { platform }), + ...(options?.removeResultType + ? resultType + : { ...(snapshot ? { snapshot } : {}), ...(removed ? { removed } : {}) }), }; return acc; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts index 206719927281..4ad6146328a1 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty, pickBy, some } from 'lodash'; +import { isEmpty, pickBy, some, isBoolean } from 'lodash'; import type { IRouter } from '@kbn/core/server'; import { PLUGIN_ID } from '../../../common'; import type { CreateSavedQueryRequestSchemaDecoded } from '../../../common/schemas/routes/saved_query/create_saved_query_request_schema'; @@ -76,7 +76,7 @@ export const createSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp updated_by: currentUser, updated_at: new Date().toISOString(), }, - (value) => !isEmpty(value) || value === false + (value) => !isEmpty(value) || isBoolean(value) ) ); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx index 7b96f3886159..0d0143eab2e3 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/index.tsx @@ -29,7 +29,7 @@ import { LabelField } from './label_field'; import OsqueryLogo from './osquery_icon/osquery.svg'; import { OsqueryFlyout } from '../../../../../detections/components/osquery/osquery_flyout'; import { BasicAlertDataContext } from '../../../event_details/investigation_guide_view'; -import { convertECSMappingToObject } from './utils'; +import { OsqueryNotAvailablePrompt } from './not_available_prompt'; const StyledEuiButton = styled(EuiButton)` > span > img { @@ -49,7 +49,12 @@ const OsqueryEditorComponent = ({ }; }>) => { const isEditMode = node != null; - const { osquery } = useKibana().services; + const { + osquery, + application: { + capabilities: { osquery: osqueryPermissions }, + }, + } = useKibana().services; const formMethods = useForm<{ label: string; query: string; @@ -70,7 +75,7 @@ const OsqueryEditorComponent = ({ { query: data.query, label: data.label, - ecs_mapping: convertECSMappingToObject(data.ecs_mapping), + ecs_mapping: data.ecs_mapping, }, (value) => !isEmpty(value) ) @@ -83,6 +88,17 @@ const OsqueryEditorComponent = ({ [onSave] ); + const noOsqueryPermissions = useMemo( + () => + (!osqueryPermissions.runSavedQueries || !osqueryPermissions.readSavedQueries) && + !osqueryPermissions.writeLiveQueries, + [ + osqueryPermissions.readSavedQueries, + osqueryPermissions.runSavedQueries, + osqueryPermissions.writeLiveQueries, + ] + ); + const OsqueryActionForm = useMemo(() => { if (osquery?.LiveQueryField) { const { LiveQueryField } = osquery; @@ -98,6 +114,10 @@ const OsqueryEditorComponent = ({ return null; }, [formMethods, osquery]); + if (noOsqueryPermissions) { + return ; + } + return ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/not_available_prompt.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/not_available_prompt.tsx new file mode 100644 index 000000000000..90db73811bd4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/not_available_prompt.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCode, EuiEmptyPrompt } from '@elastic/eui'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { i18n } from '@kbn/i18n'; + +export const PERMISSION_DENIED = i18n.translate( + 'xpack.securitySolution.markdown.osquery.permissionDenied', + { + defaultMessage: 'Permission denied', + } +); + +export const OsqueryNotAvailablePrompt = () => ( + {PERMISSION_DENIED}

    } + titleSize="xs" + body={ +

    + {'osquery'}, + }} + /> +

    + } + /> +); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts deleted file mode 100644 index 77e2f14c5142..000000000000 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/utils.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { isEmpty, reduce } from 'lodash'; - -export const convertECSMappingToObject = ( - ecsMapping: Array<{ - key: string; - result: { - type: string; - value: string; - }; - }> -) => - reduce( - ecsMapping, - (acc, value) => { - if (!isEmpty(value?.key) && !isEmpty(value.result?.type) && !isEmpty(value.result?.value)) { - acc[value.key] = { - [value.result.type]: value.result.value, - }; - } - - return acc; - }, - {} as Record - ); diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts index bef18d017722..7ee2680e21f8 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.5.0-SNAPSHOT'; + return '8.6.0-SNAPSHOT'; } From 659ba3b69e34f072bdeef626e7830689307c25f2 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 28 Sep 2022 15:33:11 -0500 Subject: [PATCH 085/185] [data views] Allow noop on REST API update (#141875) * allow data view update noop --- .../rest_api_routes/update_data_view.ts | 36 +++++++++---------- .../update_data_view/errors.ts | 6 ++-- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/plugins/data_views/server/rest_api_routes/update_data_view.ts b/src/plugins/data_views/server/rest_api_routes/update_data_view.ts index 0e9f0983ac64..1ac504ac652b 100644 --- a/src/plugins/data_views/server/rest_api_routes/update_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/update_data_view.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { IRouter, StartServicesAccessor } from '@kbn/core/server'; -import { DataViewsService } from '../../common/data_views'; +import { DataViewsService, DataView } from '../../common/data_views'; import { DataViewSpec } from '../../common/types'; import { handleErrors } from './util/handle_errors'; import { fieldSpecSchema, runtimeFieldSchema, serializedFieldFormatSchema } from './util/schemas'; @@ -71,46 +71,46 @@ export const updateDataView = async ({ name, } = spec; - let changeCount = 0; + let isChanged = false; let doRefreshFields = false; if (title !== undefined && title !== dataView.title) { - changeCount++; + isChanged = true; dataView.title = title; } if (timeFieldName !== undefined && timeFieldName !== dataView.timeFieldName) { - changeCount++; + isChanged = true; dataView.timeFieldName = timeFieldName; } if (sourceFilters !== undefined) { - changeCount++; + isChanged = true; dataView.sourceFilters = sourceFilters; } if (fieldFormats !== undefined) { - changeCount++; + isChanged = true; dataView.fieldFormatMap = fieldFormats; } if (type !== undefined) { - changeCount++; + isChanged = true; dataView.type = type; } if (typeMeta !== undefined) { - changeCount++; + isChanged = true; dataView.typeMeta = typeMeta; } if (name !== undefined) { - changeCount++; + isChanged = true; dataView.name = name; } if (fields !== undefined) { - changeCount++; + isChanged = true; doRefreshFields = true; dataView.fields.replaceAll( Object.values(fields || {}).map((field) => ({ @@ -122,19 +122,19 @@ export const updateDataView = async ({ } if (runtimeFieldMap !== undefined) { - changeCount++; + isChanged = true; dataView.replaceAllRuntimeFields(runtimeFieldMap); } - if (changeCount < 1) { - throw new Error('Index pattern change set is empty.'); - } - - await dataViewsService.updateSavedObject(dataView); + if (isChanged) { + const result = (await dataViewsService.updateSavedObject(dataView)) as DataView; - if (doRefreshFields && refreshFields) { - await dataViewsService.refreshFields(dataView); + if (doRefreshFields && refreshFields) { + await dataViewsService.refreshFields(dataView); + } + return result; } + return dataView; }; diff --git a/test/api_integration/apis/data_views/data_views_crud/update_data_view/errors.ts b/test/api_integration/apis/data_views/data_views_crud/update_data_view/errors.ts index 54f61cba1cfb..670b003a3d24 100644 --- a/test/api_integration/apis/data_views/data_views_crud/update_data_view/errors.ts +++ b/test/api_integration/apis/data_views/data_views_crud/update_data_view/errors.ts @@ -53,7 +53,7 @@ export default function ({ getService }: FtrProviderContext) { ); }); - it('returns error when update patch is empty', async () => { + it('returns success when update patch is empty', async () => { const title1 = `foo-${Date.now()}-${Math.random()}*`; const response = await supertest.post(config.path).send({ [config.serviceKey]: { @@ -65,9 +65,7 @@ export default function ({ getService }: FtrProviderContext) { [config.serviceKey]: {}, }); - expect(response2.status).to.be(400); - expect(response2.body.statusCode).to.be(400); - expect(response2.body.message).to.be('Index pattern change set is empty.'); + expect(response2.status).to.be(200); }); }); }); From 630c96bfd9c62f0aaad7c0d3c40cacdcaf161de1 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 28 Sep 2022 23:18:02 +0200 Subject: [PATCH 086/185] [ML] Replace the local storage hook (#141968) * replace useStorage usage * fix test mock * unit tests * update tests * test for storage event --- .../full_time_range_selector.test.tsx | 2 +- .../full_time_range_selector.tsx | 2 +- .../components/job_selector/job_selector.tsx | 2 +- .../application/contexts/ml/use_storage.ts | 50 ------ .../contexts/storage/storage_context.test.tsx | 144 ++++++++++++++++++ .../contexts/storage/storage_context.tsx | 28 +++- .../public/application/explorer/explorer.tsx | 2 +- .../components/getting_started_callout.tsx | 2 +- .../series_controls/series_controls.tsx | 2 +- 9 files changed, 170 insertions(+), 64 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/contexts/ml/use_storage.ts create mode 100644 x-pack/plugins/ml/public/application/contexts/storage/storage_context.test.tsx diff --git a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.test.tsx b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.test.tsx index 0d20267f33ec..7566d3664af6 100644 --- a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.test.tsx +++ b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.test.tsx @@ -20,7 +20,7 @@ jest.mock('./full_time_range_selector_service', () => ({ mockSetFullTimeRange(indexPattern, query), })); -jest.mock('../../contexts/ml/use_storage', () => { +jest.mock('../../contexts/storage', () => { return { useStorage: jest.fn(() => 'exclude-frozen'), }; diff --git a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.tsx b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.tsx index 06bb873971ba..9b9154eb3366 100644 --- a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector.tsx @@ -23,7 +23,7 @@ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/type import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; import { setFullTimeRange } from './full_time_range_selector_service'; -import { useStorage } from '../../contexts/ml/use_storage'; +import { useStorage } from '../../contexts/storage'; import { ML_FROZEN_TIER_PREFERENCE } from '../../../../common/types/storage'; import { GetTimeFieldRangeResponse } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx index 79d33ef9cd2a..16fbc81b23f1 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx @@ -28,7 +28,7 @@ import { JobSelectorFlyoutProps, } from './job_selector_flyout'; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; -import { useStorage } from '../../contexts/ml/use_storage'; +import { useStorage } from '../../contexts/storage'; import { ML_APPLY_TIME_RANGE_CONFIG } from '../../../../common/types/storage'; interface GroupObj { diff --git a/x-pack/plugins/ml/public/application/contexts/ml/use_storage.ts b/x-pack/plugins/ml/public/application/contexts/ml/use_storage.ts deleted file mode 100644 index 761434a7bb3b..000000000000 --- a/x-pack/plugins/ml/public/application/contexts/ml/use_storage.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useCallback, useState } from 'react'; -import { isDefined } from '../../../../common/types/guards'; -import { useMlKibana } from '../kibana'; -import type { MlStorageKey } from '../../../../common/types/storage'; -import { TMlStorageMapped } from '../../../../common/types/storage'; - -/** - * Hook for accessing and changing a value in the storage. - * @param key - Storage key - * @param initValue - */ -export function useStorage>( - key: K, - initValue?: T -): [ - typeof initValue extends undefined - ? TMlStorageMapped - : Exclude, undefined>, - (value: TMlStorageMapped) => void -] { - const { - services: { storage }, - } = useMlKibana(); - - const [val, setVal] = useState(storage.get(key) ?? initValue); - - const setStorage = useCallback((value: TMlStorageMapped): void => { - try { - if (isDefined(value)) { - storage.set(key, value); - setVal(value); - } else { - storage.remove(key); - setVal(initValue); - } - } catch (e) { - throw new Error('Unable to update storage with provided value'); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return [val, setStorage]; -} diff --git a/x-pack/plugins/ml/public/application/contexts/storage/storage_context.test.tsx b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.test.tsx new file mode 100644 index 000000000000..6aeeb396f38c --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.test.tsx @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { MlStorageContextProvider, useStorage } from './storage_context'; +import { MlStorageKey } from '../../../../common/types/storage'; + +const mockSet = jest.fn(); +const mockRemove = jest.fn(); + +jest.mock('../kibana', () => ({ + useMlKibana: () => { + return { + services: { + storage: { + set: mockSet, + get: jest.fn((key: MlStorageKey) => { + switch (key) { + case 'ml.gettingStarted.isDismissed': + return true; + default: + return; + } + }), + remove: mockRemove, + }, + }, + }; + }, +})); + +describe('useStorage', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('returns the default value', () => { + const { result } = renderHook(() => useStorage('ml.jobSelectorFlyout.applyTimeRange', true), { + wrapper: MlStorageContextProvider, + }); + + expect(result.current[0]).toBe(true); + }); + + test('returns the value from storage', () => { + const { result } = renderHook(() => useStorage('ml.gettingStarted.isDismissed', false), { + wrapper: MlStorageContextProvider, + }); + + expect(result.current[0]).toBe(true); + }); + + test('updates the storage value', async () => { + const { result, waitForNextUpdate } = renderHook( + () => useStorage('ml.gettingStarted.isDismissed'), + { + wrapper: MlStorageContextProvider, + } + ); + + const [value, setValue] = result.current; + + expect(value).toBe(true); + + await act(async () => { + setValue(false); + await waitForNextUpdate(); + }); + + expect(result.current[0]).toBe(false); + expect(mockSet).toHaveBeenCalledWith('ml.gettingStarted.isDismissed', false); + }); + + test('removes the storage value', async () => { + const { result, waitForNextUpdate } = renderHook( + () => useStorage('ml.gettingStarted.isDismissed'), + { + wrapper: MlStorageContextProvider, + } + ); + + const [value, setValue] = result.current; + + expect(value).toBe(true); + + await act(async () => { + setValue(undefined); + await waitForNextUpdate(); + }); + + expect(result.current[0]).toBe(undefined); + expect(mockRemove).toHaveBeenCalledWith('ml.gettingStarted.isDismissed'); + }); + + test('updates the value on storage event', async () => { + const { result, waitForNextUpdate } = renderHook( + () => useStorage('ml.gettingStarted.isDismissed'), + { + wrapper: MlStorageContextProvider, + } + ); + + expect(result.current[0]).toBe(true); + + await act(async () => { + window.dispatchEvent( + new StorageEvent('storage', { + key: 'test_key', + newValue: 'test_value', + }) + ); + }); + + expect(result.current[0]).toBe(true); + + await act(async () => { + window.dispatchEvent( + new StorageEvent('storage', { + key: 'ml.gettingStarted.isDismissed', + newValue: null, + }) + ); + await waitForNextUpdate(); + }); + + expect(result.current[0]).toBe(undefined); + + await act(async () => { + window.dispatchEvent( + new StorageEvent('storage', { + key: 'ml.gettingStarted.isDismissed', + newValue: 'false', + }) + ); + await waitForNextUpdate(); + }); + + expect(result.current[0]).toBe(false); + }); +}); diff --git a/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx index ccd46c446ed2..c2b00a176c08 100644 --- a/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx +++ b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx @@ -69,7 +69,8 @@ export const MlStorageContextProvider: FC = ({ children }) => { setState((prev) => { return { ...prev, - [event.key as MlStorageKey]: event.newValue, + [event.key as MlStorageKey]: + typeof event.newValue === 'string' ? JSON.parse(event.newValue) : event.newValue, }; }); } else { @@ -106,21 +107,32 @@ export const MlStorageContextProvider: FC = ({ children }) => { * @param key * @param initValue */ -export function useStorage( +export function useStorage>( key: K, - initValue?: TMlStorageMapped -): [TMlStorageMapped | undefined, (value: TMlStorageMapped) => void] { - const { value, setValue } = useContext(MlStorageContext); + initValue?: T +): [ + typeof initValue extends undefined + ? TMlStorageMapped | undefined + : Exclude, undefined>, + (value: TMlStorageMapped) => void +] { + const { value, setValue, removeValue } = useContext(MlStorageContext); const resultValue = useMemo(() => { - return (value?.[key] ?? initValue) as TMlStorageMapped; + return (value?.[key] ?? initValue) as typeof initValue extends undefined + ? TMlStorageMapped | undefined + : Exclude, undefined>; }, [value, key, initValue]); const setVal = useCallback( (v: TMlStorageMapped) => { - setValue(key, v); + if (isDefined(v)) { + setValue(key, v); + } else { + removeValue(key); + } }, - [setValue, key] + [setValue, removeValue, key] ); return [resultValue, setVal]; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.tsx b/x-pack/plugins/ml/public/application/explorer/explorer.tsx index b9feffb1ec11..5fcab05f068b 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer.tsx @@ -78,7 +78,7 @@ import { useMlKibana, useMlLocator } from '../contexts/kibana'; import { useMlContext } from '../contexts/ml'; import { useAnomalyExplorerContext } from './anomaly_explorer_context'; import { ML_ANOMALY_EXPLORER_PANELS } from '../../../common/types/storage'; -import { useStorage } from '../contexts/ml/use_storage'; +import { useStorage } from '../contexts/storage'; interface ExplorerPageProps { jobSelectorProps: JobSelectorProps; diff --git a/x-pack/plugins/ml/public/application/overview/components/getting_started_callout.tsx b/x-pack/plugins/ml/public/application/overview/components/getting_started_callout.tsx index 457bca80ee2c..7b1f380c9065 100644 --- a/x-pack/plugins/ml/public/application/overview/components/getting_started_callout.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/getting_started_callout.tsx @@ -9,7 +9,7 @@ import React, { FC } from 'react'; import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useMlKibana } from '../../contexts/kibana'; -import { useStorage } from '../../contexts/ml/use_storage'; +import { useStorage } from '../../contexts/storage'; import { ML_GETTING_STARTED_CALLOUT_DISMISSED } from '../../../../common/types/storage'; const feedbackLink = 'https://www.elastic.co/community/'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx index 0df10505e73c..23084c8d8886 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx @@ -26,7 +26,7 @@ import { PartitionFieldConfig, PartitionFieldsConfig, } from '../../../../../common/types/storage'; -import { useStorage } from '../../../contexts/ml/use_storage'; +import { useStorage } from '../../../contexts/storage'; import { EntityFieldType } from '../../../../../common/types/anomalies'; import { FieldDefinition } from '../../../services/results_service/result_service_rx'; import { getViewableDetectors } from '../../timeseriesexplorer_utils/get_viewable_detectors'; From 598aa024bfabfb7a658afc13ad438dbdd304a32d Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Wed, 28 Sep 2022 14:29:56 -0700 Subject: [PATCH 087/185] [8.5][Elastic Defend onboarding] Disabling protections for Cloud (#141879) --- .../endpoint_policy_create_extension.tsx | 55 +------------------ .../handlers/create_default_policy.ts | 31 ++--------- .../handlers/validate_integration_config.ts | 12 ---- .../server/fleet_integration/types.ts | 6 -- 4 files changed, 7 insertions(+), 97 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx index d1223ff14826..0617707505e5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_create_extension/endpoint_policy_create_extension.tsx @@ -8,7 +8,6 @@ import React, { memo, useState, useEffect, useRef, useCallback } from 'react'; import { EuiForm, - EuiCheckbox, EuiRadio, EuiSelect, EuiText, @@ -19,7 +18,6 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import styled from 'styled-components'; import type { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public'; -import { useLicense } from '../../../../../../common/hooks/use_license'; import { ALL_EVENTS, CLOUD_SECURITY, @@ -28,7 +26,6 @@ import { EDR_ESSENTIAL, ENDPOINT, INTERACTIVE_ONLY, - PREVENT_MALICIOUS_BEHAVIOR, } from './translations'; const PREFIX = 'endpoint_policy_create_extension'; @@ -70,11 +67,8 @@ const HelpTextWithPadding = styled.div` */ export const EndpointPolicyCreateExtension = memo( ({ newPolicy, onChange }) => { - const isPlatinumPlus = useLicense().isPlatinumPlus(); - // / Endpoint Radio Options (NGAV and EDRs) const [endpointPreset, setEndpointPreset] = useState('NGAV'); - const [behaviorProtectionChecked, setBehaviorProtectionChecked] = useState(false); const [selectedCloudEvent, setSelectedCloudEvent] = useState('ALL_EVENTS'); const [selectedEnvironment, setSelectedEnvironment] = useState('endpoint'); const initialRender = useRef(true); @@ -130,11 +124,6 @@ export const EndpointPolicyCreateExtension = memo) => { setSelectedEnvironment(e?.target?.value as Environment); @@ -170,9 +152,6 @@ export const EndpointPolicyCreateExtension = memo) => { setEndpointPreset(e.target.value as EndpointPreset); }, []); - const onChangeMaliciousBehavior = useCallback((e: React.ChangeEvent) => { - setBehaviorProtectionChecked((checked) => !checked); - }, []); const getEndpointPresetsProps = useCallback( (preset: EndpointPreset) => ({ @@ -217,7 +196,7 @@ export const EndpointPolicyCreateExtension = memo ), @@ -327,36 +306,6 @@ export const EndpointPolicyCreateExtension = memo - {isPlatinumPlus && ( - <> - - -

    - -

    -
    - - -

    - -

    -
    - - - - )} )} diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.ts index 8ffd33e070d9..a2da989a9c08 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.ts @@ -13,11 +13,7 @@ import type { LicenseService } from '../../../common/license/license'; import { isAtLeast } from '../../../common/license/license'; import { ProtectionModes } from '../../../common/endpoint/types'; import type { PolicyConfig } from '../../../common/endpoint/types'; -import type { - AnyPolicyCreateConfig, - PolicyCreateCloudConfig, - PolicyCreateEndpointConfig, -} from '../types'; +import type { AnyPolicyCreateConfig, PolicyCreateEndpointConfig } from '../types'; import { ENDPOINT_CONFIG_PRESET_EDR_ESSENTIAL, ENDPOINT_CONFIG_PRESET_NGAV } from '../constants'; /** @@ -32,7 +28,7 @@ export const createDefaultPolicy = ( : policyConfigFactoryWithoutPaidFeatures(); if (config?.type === 'cloud') { - return getCloudPolicyWithIntegrationConfig(policy, config); + return getCloudPolicyConfig(policy); } return getEndpointPolicyWithIntegrationConfig(policy, config); @@ -95,37 +91,20 @@ const getEndpointPolicyWithIntegrationConfig = ( /** * Retrieve policy for cloud based on the on the cloud integration config */ -const getCloudPolicyWithIntegrationConfig = ( - policy: PolicyConfig, - config: PolicyCreateCloudConfig -): PolicyConfig => { - /** - * Check if the protection is supported, then retrieve Behavior Protection mode based on cloud settings - */ - const getBehaviorProtectionMode = () => { - if (!policy.linux.behavior_protection.supported) { - return ProtectionModes.off; - } - - return config.cloudConfig.preventions.behavior_protection - ? ProtectionModes.prevent - : ProtectionModes.off; - }; - +const getCloudPolicyConfig = (policy: PolicyConfig): PolicyConfig => { + // Disabling all protections, since it's not yet supported on Cloud integrations const protections = { - // Disabling memory_protection, since it's not supported on Cloud integrations memory_protection: { supported: false, mode: ProtectionModes.off, }, malware: { ...policy.linux.malware, - // Disabling Malware protection, since it's not supported on Cloud integrations due to performance reasons mode: ProtectionModes.off, }, behavior_protection: { ...policy.linux.behavior_protection, - mode: getBehaviorProtectionMode(), + mode: ProtectionModes.off, }, }; diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/validate_integration_config.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/validate_integration_config.ts index 76bd3e8af93f..bc5835856581 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/validate_integration_config.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/validate_integration_config.ts @@ -46,18 +46,6 @@ const validateEndpointIntegrationConfig = ( } }; const validateCloudIntegrationConfig = (config: PolicyCreateCloudConfig, logger: Logger): void => { - if (!config?.cloudConfig?.preventions) { - logger.warn( - 'missing cloudConfig preventions: {preventions : behavior_protection: true / false}' - ); - throwError('invalid value for cloudConfig: missing preventions '); - } - if (typeof config.cloudConfig.preventions.behavior_protection !== 'boolean') { - logger.warn( - `invalid value for cloudConfig preventions behavior_protection: ${config.cloudConfig.preventions.behavior_protection}` - ); - throwError('invalid value for cloudConfig preventions behavior_protection'); - } if (!config?.eventFilters) { logger.warn( `eventFilters is required for cloud integration: {eventFilters : nonInteractiveSession: true / false}` diff --git a/x-pack/plugins/security_solution/server/fleet_integration/types.ts b/x-pack/plugins/security_solution/server/fleet_integration/types.ts index 2d012832f1ae..0c7a7c17951e 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/types.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/types.ts @@ -18,12 +18,6 @@ export interface PolicyCreateEventFilters { export interface PolicyCreateCloudConfig { type: 'cloud'; - cloudConfig: { - preventions: { - malware: boolean; - behavior_protection: boolean; - }; - }; eventFilters?: PolicyCreateEventFilters; } From 74a12296237adeffdb3980b46e6a293cc7845f94 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Wed, 28 Sep 2022 17:36:32 -0400 Subject: [PATCH 088/185] [Synthetics] Validation for multi url hosts/urls + more (#141668) * synthetics - project monitors - add additional validation for urls/hosts and request.check.body * add normalizing for service.name and ssl.supported_protocols and additional tests * adjust types * adjust tests * adjust types * remove apmServiceName from browser monitors * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * adjust tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/formatters/browser/formatters.ts | 1 - .../common/formatters/common/formatters.ts | 1 + .../monitor_management/monitor_types.ts | 2 +- .../monitor_types_project.ts | 1 - .../fleet_package/browser/normalizers.ts | 1 - .../fleet_package/common/normalizers.ts | 1 + .../synthetics_service/formatters/browser.ts | 1 - .../synthetics_service/formatters/common.ts | 1 + .../normalizers/browser_monitor.test.ts | 16 +- .../normalizers/browser_monitor.ts | 23 +- .../normalizers/common_fields.test.ts | 66 +++++ .../normalizers/common_fields.ts | 65 ++++- .../normalizers/http_monitor.test.ts | 242 ++++++++++++++++ .../normalizers/http_monitor.ts | 63 ++++- .../normalizers/icmp_monitor.test.ts | 227 +++++++++++++++ .../normalizers/icmp_monitor.ts | 33 ++- .../project_monitor/normalizers/index.ts | 20 +- .../normalizers/tcp_monitor.test.ts | 265 ++++++++++++++++++ .../normalizers/tcp_monitor.ts | 39 ++- .../project_monitor_formatter.ts | 13 +- .../apis/uptime/rest/add_monitor_project.ts | 21 +- .../rest/fixtures/project_http_monitor.json | 7 +- .../rest/fixtures/project_icmp_monitor.json | 5 +- .../rest/fixtures/project_tcp_monitor.json | 9 +- 24 files changed, 1029 insertions(+), 94 deletions(-) create mode 100644 x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts create mode 100644 x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts create mode 100644 x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts create mode 100644 x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index f05003f650de..78e55e97d5b0 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -74,7 +74,6 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKey.IGNORE_HTTPS_ERRORS]: null, [ConfigKey.PROJECT_ID]: null, [ConfigKey.PLAYWRIGHT_OPTIONS]: null, - [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, [ConfigKey.ORIGINAL_SPACE]: null, [ConfigKey.TEXT_ASSERTION]: null, ...commonFormatters, diff --git a/x-pack/plugins/synthetics/common/formatters/common/formatters.ts b/x-pack/plugins/synthetics/common/formatters/common/formatters.ts index 89bf8793302b..751721e5d703 100644 --- a/x-pack/plugins/synthetics/common/formatters/common/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/common/formatters.ts @@ -29,6 +29,7 @@ export const commonFormatters: CommonFormatMap = { [ConfigKey.MONITOR_SOURCE_TYPE]: null, [ConfigKey.FORM_MONITOR_TYPE]: null, [ConfigKey.JOURNEY_ID]: null, + [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, }; export const arrayToJsonFormatter = (value: string[] = []) => diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 58e3a31df8a3..46bc0f135452 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -83,6 +83,7 @@ export const CommonFieldsCodec = t.intersection([ [ConfigKey.MONITOR_SOURCE_TYPE]: SourceTypeCodec, [ConfigKey.CONFIG_ID]: t.string, [ConfigKey.JOURNEY_ID]: t.string, + [ConfigKey.CUSTOM_HEARTBEAT_ID]: t.string, }), ]); @@ -218,7 +219,6 @@ export const EncryptedBrowserSimpleFieldsCodec = t.intersection([ [ConfigKey.PLAYWRIGHT_OPTIONS]: t.string, [ConfigKey.PROJECT_ID]: t.string, [ConfigKey.ORIGINAL_SPACE]: t.string, - [ConfigKey.CUSTOM_HEARTBEAT_ID]: t.string, [ConfigKey.TEXT_ASSERTION]: t.string, }), ]), diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts index 6abcf4b83213..cba2f506189e 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts @@ -30,7 +30,6 @@ export const ProjectMonitorCodec = t.intersection([ screenshot: ScreenshotOptionCodec, tags: t.union([t.string, t.array(t.string)]), ignoreHTTPSErrors: t.boolean, - apmServiceName: t.string, playwrightOptions: t.record(t.string, t.unknown), filter: t.interface({ match: t.string, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts index a8b59b16a346..61f978327b8a 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts @@ -109,7 +109,6 @@ export const browserNormalizers: BrowserNormalizerMap = { [ConfigKey.IGNORE_HTTPS_ERRORS]: getBrowserNormalizer(ConfigKey.IGNORE_HTTPS_ERRORS), [ConfigKey.PROJECT_ID]: getBrowserNormalizer(ConfigKey.PROJECT_ID), [ConfigKey.PLAYWRIGHT_OPTIONS]: getBrowserNormalizer(ConfigKey.PLAYWRIGHT_OPTIONS), - [ConfigKey.CUSTOM_HEARTBEAT_ID]: getBrowserNormalizer(ConfigKey.CUSTOM_HEARTBEAT_ID), [ConfigKey.ORIGINAL_SPACE]: getBrowserNormalizer(ConfigKey.ORIGINAL_SPACE), [ConfigKey.TEXT_ASSERTION]: getBrowserNormalizer(ConfigKey.TEXT_ASSERTION), ...commonNormalizers, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts index 14fab3caeb13..630ac70a8e05 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts @@ -93,4 +93,5 @@ export const commonNormalizers: CommonNormalizerMap = { [ConfigKey.MONITOR_SOURCE_TYPE]: getCommonNormalizer(ConfigKey.MONITOR_SOURCE_TYPE), [ConfigKey.FORM_MONITOR_TYPE]: getCommonNormalizer(ConfigKey.FORM_MONITOR_TYPE), [ConfigKey.JOURNEY_ID]: getCommonNormalizer(ConfigKey.JOURNEY_ID), + [ConfigKey.CUSTOM_HEARTBEAT_ID]: getCommonNormalizer(ConfigKey.CUSTOM_HEARTBEAT_ID), }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts index 9c9d24a3f58f..7095e437aea2 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts @@ -69,7 +69,6 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKey.PROJECT_ID]: null, [ConfigKey.PLAYWRIGHT_OPTIONS]: (fields) => stringToObjectFormatter(fields[ConfigKey.PLAYWRIGHT_OPTIONS] || ''), - [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, [ConfigKey.ORIGINAL_SPACE]: null, [ConfigKey.TEXT_ASSERTION]: null, ...commonFormatters, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts index 16a0829cbb71..f9c3125041b4 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts @@ -31,6 +31,7 @@ export const commonFormatters: CommonFormatMap = { fields[ConfigKey.MONITOR_SOURCE_TYPE] || SourceType.UI, [ConfigKey.FORM_MONITOR_TYPE]: null, [ConfigKey.JOURNEY_ID]: null, + [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, }; export const arrayFormatter = (value: string[] = []) => (value.length ? value : null); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts index ed02f1037e32..9b32b61a59b3 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts @@ -67,9 +67,7 @@ describe('browser normalizers', () => { locations: ['us_central'], tags: ['tag1', 'tag2'], ignoreHTTPSErrors: true, - apmServiceName: 'cart-service', - type: DataStream.BROWSER, - }, + } as ProjectMonitor, // test that normalizers defaults to browser when type is omitted { id: 'test-id-2', screenshot: ScreenshotOption.ON, @@ -86,7 +84,6 @@ describe('browser normalizers', () => { locations: ['us_central', 'us_east'], tags: ['tag3', 'tag4'], ignoreHTTPSErrors: false, - apmServiceName: 'bean-service', type: DataStream.BROWSER, }, { @@ -106,7 +103,6 @@ describe('browser normalizers', () => { privateLocations: ['Germany'], tags: ['tag3', 'tag4'], ignoreHTTPSErrors: false, - apmServiceName: 'bean-service', type: DataStream.BROWSER, }, ]; @@ -118,6 +114,7 @@ describe('browser normalizers', () => { monitors, projectId, namespace: 'test-space', + version: '8.5.0', }); expect(actual).toEqual([ { @@ -145,7 +142,7 @@ describe('browser normalizers', () => { unit: 'm', }, screenshots: 'off', - 'service.name': 'cart-service', + 'service.name': '', 'source.project.content': 'test content 1', tags: ['tag1', 'tag2'], 'throttling.config': '5d/10u/20l', @@ -162,6 +159,7 @@ describe('browser normalizers', () => { timeout: null, }, unsupportedKeys: [], + errors: [], }, { normalizedFields: { @@ -201,7 +199,7 @@ describe('browser normalizers', () => { unit: 'm', }, screenshots: 'on', - 'service.name': 'bean-service', + 'service.name': '', 'source.project.content': 'test content 2', tags: ['tag3', 'tag4'], 'throttling.config': '10d/15u/18l', @@ -217,6 +215,7 @@ describe('browser normalizers', () => { timeout: null, }, unsupportedKeys: [], + errors: [], }, { normalizedFields: { @@ -263,7 +262,7 @@ describe('browser normalizers', () => { unit: 'm', }, screenshots: 'on', - 'service.name': 'bean-service', + 'service.name': '', 'source.project.content': 'test content 3', tags: ['tag3', 'tag4'], 'throttling.config': '10d/15u/18l', @@ -279,6 +278,7 @@ describe('browser normalizers', () => { timeout: null, }, unsupportedKeys: [], + errors: [], }, ]); }); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts index aae7031435c7..8eba2cd2651d 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts @@ -10,20 +10,14 @@ import { ConfigKey, DataStream, FormMonitorType, - Locations, - PrivateLocation, - ProjectMonitor, } from '../../../../common/runtime_types'; -import { getNormalizeCommonFields, getValueInSeconds } from './common_fields'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; - -export interface NormalizedProjectProps { - locations: Locations; - privateLocations: PrivateLocation[]; - monitor: ProjectMonitor; - projectId: string; - namespace: string; -} +import { + NormalizedProjectProps, + NormalizerResult, + getNormalizeCommonFields, + getValueInSeconds, +} from './common_fields'; export const getNormalizeBrowserFields = ({ locations = [], @@ -31,7 +25,8 @@ export const getNormalizeBrowserFields = ({ monitor, projectId, namespace, -}: NormalizedProjectProps): { normalizedFields: BrowserFields; unsupportedKeys: string[] } => { + version, +}: NormalizedProjectProps): NormalizerResult => { const defaultFields = DEFAULT_FIELDS[DataStream.BROWSER]; const commonFields = getNormalizeCommonFields({ @@ -40,6 +35,7 @@ export const getNormalizeBrowserFields = ({ monitor, projectId, namespace, + version, }); const normalizedFields = { @@ -81,5 +77,6 @@ export const getNormalizeBrowserFields = ({ ...normalizedFields, }, unsupportedKeys: [], + errors: [], }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts new file mode 100644 index 000000000000..fee6f0ca2637 --- /dev/null +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { flattenAndFormatObject } from './common_fields'; + +describe('normalizeYamlConfig', () => { + it('does not continue flattening when encountering an array', () => { + const array = ['value1', 'value2']; + const supportedKeys: string[] = []; + const nestedObject = { + a: { + nested: { + key: array, + }, + }, + }; + expect(flattenAndFormatObject(nestedObject, '', supportedKeys)).toEqual({ + 'a.nested.key': array, + }); + }); + + it('does not continue flattening when encountering a supported key', () => { + const supportedKeys: string[] = ['a.supported.key']; + const object = { + with: { + further: { + nesting: '', + }, + }, + }; + const nestedObject = { + a: { + supported: { + key: object, + }, + }, + }; + expect(flattenAndFormatObject(nestedObject, '', supportedKeys)).toEqual({ + 'a.supported.key': object, + }); + }); + + it('flattens objects', () => { + const supportedKeys: string[] = []; + const nestedObject = { + a: { + nested: { + key: 'value1', + }, + }, + b: { + nested: { + key: 'value2', + }, + }, + }; + expect(flattenAndFormatObject(nestedObject, '', supportedKeys)).toEqual({ + 'a.nested.key': 'value1', + 'b.nested.key': 'value2', + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts index 31aebd0e8586..56045712606a 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts @@ -20,7 +20,27 @@ import { } from '../../../../common/runtime_types'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { DEFAULT_COMMON_FIELDS } from '../../../../common/constants/monitor_defaults'; -import { NormalizedProjectProps } from '.'; + +export interface NormalizedProjectProps { + locations: Locations; + privateLocations: PrivateLocation[]; + monitor: ProjectMonitor; + projectId: string; + namespace: string; + version: string; +} + +export interface Error { + id: string; + reason: string; + details: string; +} + +export interface NormalizerResult { + normalizedFields: MonitorTypeFields; + unsupportedKeys: string[]; + errors: Error[]; +} export const getNormalizeCommonFields = ({ locations = [], @@ -28,7 +48,7 @@ export const getNormalizeCommonFields = ({ monitor, projectId, namespace, -}: NormalizedProjectProps): CommonFields => { +}: NormalizedProjectProps): Partial => { const defaultFields = DEFAULT_COMMON_FIELDS; const normalizedFields = { @@ -45,18 +65,16 @@ export const getNormalizeCommonFields = ({ privateLocations, publicLocations: locations, }), - [ConfigKey.APM_SERVICE_NAME]: - monitor.apmServiceName || defaultFields[ConfigKey.APM_SERVICE_NAME], [ConfigKey.TAGS]: getOptionalListField(monitor.tags) || defaultFields[ConfigKey.TAGS], [ConfigKey.NAMESPACE]: formatKibanaNamespace(namespace) || defaultFields[ConfigKey.NAMESPACE], [ConfigKey.ORIGINAL_SPACE]: namespace || defaultFields[ConfigKey.NAMESPACE], [ConfigKey.CUSTOM_HEARTBEAT_ID]: getCustomHeartbeatId(monitor, projectId, namespace), [ConfigKey.ENABLED]: monitor.enabled ?? defaultFields[ConfigKey.ENABLED], + [ConfigKey.TIMEOUT]: monitor.timeout + ? getValueInSeconds(monitor.timeout) + : defaultFields[ConfigKey.TIMEOUT], }; - return { - ...defaultFields, - ...normalizedFields, - }; + return normalizedFields; }; export const getCustomHeartbeatId = ( @@ -94,6 +112,35 @@ export const getMonitorLocations = ({ ) as BrowserFields[ConfigKey.LOCATIONS]; }; +export const getUnsupportedKeysError = ( + monitor: ProjectMonitor, + unsupportedKeys: string[], + version: string +) => ({ + id: monitor.id, + reason: 'Unsupported Heartbeat option', + details: `The following Heartbeat options are not supported for ${ + monitor.type + } project monitors in ${version}: ${unsupportedKeys.join( + '|' + )}. You monitor was not created or updated.`, +}); + +export const getMultipleUrlsOrHostsError = ( + monitor: ProjectMonitor, + key: 'hosts' | 'urls', + version: string +) => ({ + id: monitor.id, + reason: 'Unsupported Heartbeat option', + details: `Multiple ${key} are not supported for ${ + monitor.type + } project monitors in ${version}. Please set only 1 ${key.slice( + 0, + -1 + )} per monitor. You monitor was not created or updated.`, +}); + export const getValueInSeconds = (value: string) => { const keyMap = { h: 60 * 60, @@ -136,7 +183,7 @@ export const getOptionalArrayField = (value: string[] | string = '') => { * @param {Object} [monitor] * @returns {Object} Returns an object containing synthetics-compatible configuration keys */ -const flattenAndFormatObject = (obj: Record, prefix = '', keys: string[]) => +export const flattenAndFormatObject = (obj: Record, prefix = '', keys: string[]) => Object.keys(obj).reduce>((acc, k) => { const pre = prefix.length ? prefix + '.' : ''; const key = pre + k; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts new file mode 100644 index 000000000000..922c5ca6dab0 --- /dev/null +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts @@ -0,0 +1,242 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { Locations, LocationStatus, PrivateLocation } from '../../../../common/runtime_types'; +import { normalizeProjectMonitors } from '.'; + +describe('http normalizers', () => { + describe('normalize push monitors', () => { + const projectId = 'test-project-id'; + const locations: Locations = [ + { + id: 'us_central', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + { + id: 'us_east', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + ]; + const privateLocations: PrivateLocation[] = [ + { + id: 'germany', + label: 'Germany', + isServiceManaged: false, + concurrentMonitors: 1, + agentPolicyId: 'germany', + }, + ]; + const monitors = [ + { + locations: ['localhost'], + type: 'http', + enabled: false, + id: 'my-monitor-2', + name: 'My Monitor 2', + urls: ['http://localhost:9200', 'http://anotherurl:9200'], + schedule: 60, + timeout: '80s', + 'check.request': { + method: 'POST', + body: { + json: 'body', + }, + headers: { + 'a-header': 'a-header-value', + }, + }, + response: { + include_body: 'always', + }, + 'response.include_headers': false, + 'check.response': { + status: [200], + body: ['Saved', 'saved'], + }, + unsupportedKey: { + nestedUnsupportedKey: 'unsupportedValue', + }, + service: { + name: 'test service', + }, + ssl: { + supported_protocols: ['TLSv1.2', 'TLSv1.3'], + }, + }, + { + locations: ['localhost'], + type: 'http', + enabled: false, + id: 'my-monitor-3', + name: 'My Monitor 3', + urls: ['http://localhost:9200'], + schedule: 60, + timeout: '80s', + 'check.request': { + method: 'POST', + body: 'sometextbody', + headers: { + 'a-header': 'a-header-value', + }, + }, + response: { + include_body: 'always', + }, + tags: 'tag2,tag2', + 'response.include_headers': false, + 'check.response': { + status: [200], + body: { + positive: ['Saved', 'saved'], + }, + }, + 'service.name': 'test service', + 'ssl.supported_protocols': 'TLSv1.2,TLSv1.3', + }, + ]; + + it('properly normalizes http monitors', () => { + const actual = normalizeProjectMonitors({ + locations, + privateLocations, + monitors, + projectId, + namespace: 'test-space', + version: '8.5.0', + }); + expect(actual).toEqual([ + { + errors: [ + { + details: + 'Multiple urls are not supported for http project monitors in 8.5.0. Please set only 1 url per monitor. You monitor was not created or updated.', + id: 'my-monitor-2', + reason: 'Unsupported Heartbeat option', + }, + { + details: + 'The following Heartbeat options are not supported for http project monitors in 8.5.0: check.response.body|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.', + id: 'my-monitor-2', + reason: 'Unsupported Heartbeat option', + }, + ], + normalizedFields: { + __ui: { + is_tls_enabled: false, + }, + 'check.request.body': { + type: 'json', + value: '{"json":"body"}', + }, + 'check.request.headers': { + 'a-header': 'a-header-value', + }, + 'check.request.method': 'POST', + 'check.response.body.negative': [], + 'check.response.body.positive': [], + 'check.response.headers': {}, + 'check.response.status': ['200'], + config_id: '', + custom_heartbeat_id: 'my-monitor-2-test-project-id-test-space', + enabled: false, + form_monitor_type: 'http', + journey_id: 'my-monitor-2', + locations: [], + max_redirects: '0', + name: 'My Monitor 2', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + password: '', + project_id: 'test-project-id', + proxy_url: '', + 'response.include_body': 'always', + 'response.include_headers': false, + schedule: { + number: '60', + unit: 'm', + }, + 'service.name': 'test service', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: [], + timeout: '80', + type: 'http', + urls: 'http://localhost:9200', + username: '', + }, + unsupportedKeys: ['check.response.body', 'unsupportedKey.nestedUnsupportedKey'], + }, + { + errors: [], + normalizedFields: { + __ui: { + is_tls_enabled: false, + }, + 'check.request.body': { + type: 'text', + value: 'sometextbody', + }, + 'check.request.headers': { + 'a-header': 'a-header-value', + }, + 'check.request.method': 'POST', + 'check.response.body.negative': [], + 'check.response.body.positive': ['Saved', 'saved'], + 'check.response.headers': {}, + 'check.response.status': ['200'], + config_id: '', + custom_heartbeat_id: 'my-monitor-3-test-project-id-test-space', + enabled: false, + form_monitor_type: 'http', + journey_id: 'my-monitor-3', + locations: [], + max_redirects: '0', + name: 'My Monitor 3', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + password: '', + project_id: 'test-project-id', + proxy_url: '', + 'response.include_body': 'always', + 'response.include_headers': false, + schedule: { + number: '60', + unit: 'm', + }, + 'service.name': 'test service', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: ['tag2', 'tag2'], + timeout: '80', + type: 'http', + urls: 'http://localhost:9200', + username: '', + }, + unsupportedKeys: [], + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts index 6f637d818667..d994e6151782 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts @@ -4,16 +4,26 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getNormalizeCommonFields } from './common_fields'; -import { NormalizedProjectProps } from './browser_monitor'; +import { get } from 'lodash'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { ConfigKey, DataStream, FormMonitorType, HTTPFields, + Mode, + TLSVersion, } from '../../../../common/runtime_types/monitor_management'; -import { normalizeYamlConfig, getValueInSeconds, getOptionalArrayField } from './common_fields'; +import { + NormalizedProjectProps, + NormalizerResult, + getNormalizeCommonFields, + normalizeYamlConfig, + getOptionalListField, + getOptionalArrayField, + getUnsupportedKeysError, + getMultipleUrlsOrHostsError, +} from './common_fields'; export const getNormalizeHTTPFields = ({ locations = [], @@ -21,18 +31,30 @@ export const getNormalizeHTTPFields = ({ monitor, projectId, namespace, -}: NormalizedProjectProps): { normalizedFields: HTTPFields; unsupportedKeys: string[] } => { + version, +}: NormalizedProjectProps): NormalizerResult => { const defaultFields = DEFAULT_FIELDS[DataStream.HTTP]; + const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); - const commonFields = getNormalizeCommonFields({ locations, privateLocations, monitor, projectId, namespace, + version, }); + /* Check if monitor has multiple urls */ + const urls = getOptionalListField(monitor.urls); + if (urls.length > 1) { + errors.push(getMultipleUrlsOrHostsError(monitor, 'urls', version)); + } + + if (unsupportedKeys.length) { + errors.push(getUnsupportedKeysError(monitor, unsupportedKeys, version)); + } + const normalizedFields = { ...yamlConfig, ...commonFields, @@ -41,9 +63,13 @@ export const getNormalizeHTTPFields = ({ [ConfigKey.URLS]: getOptionalArrayField(monitor.urls) || defaultFields[ConfigKey.URLS], [ConfigKey.MAX_REDIRECTS]: monitor[ConfigKey.MAX_REDIRECTS] || defaultFields[ConfigKey.MAX_REDIRECTS], - [ConfigKey.TIMEOUT]: monitor.timeout - ? getValueInSeconds(monitor.timeout) - : defaultFields[ConfigKey.TIMEOUT], + [ConfigKey.REQUEST_BODY_CHECK]: getRequestBodyField( + (yamlConfig as Record)[ConfigKey.REQUEST_BODY_CHECK] as string, + defaultFields[ConfigKey.REQUEST_BODY_CHECK] + ), + [ConfigKey.TLS_VERSION]: get(monitor, ConfigKey.TLS_VERSION) + ? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[]) + : defaultFields[ConfigKey.TLS_VERSION], }; return { normalizedFields: { @@ -51,5 +77,26 @@ export const getNormalizeHTTPFields = ({ ...normalizedFields, }, unsupportedKeys, + errors, + }; +}; + +export const getRequestBodyField = ( + value: string, + defaultValue: HTTPFields[ConfigKey.REQUEST_BODY_CHECK] +): HTTPFields[ConfigKey.REQUEST_BODY_CHECK] => { + let parsedValue: string; + let type: Mode; + + if (typeof value === 'object') { + parsedValue = JSON.stringify(value); + type = Mode.JSON; + } else { + parsedValue = value; + type = Mode.PLAINTEXT; + } + return { + type, + value: parsedValue || defaultValue.value, }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts new file mode 100644 index 000000000000..e32ddf4f328a --- /dev/null +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts @@ -0,0 +1,227 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { Locations, LocationStatus, PrivateLocation } from '../../../../common/runtime_types'; +import { normalizeProjectMonitors } from '.'; + +describe('http normalizers', () => { + describe('normalize push monitors', () => { + const projectId = 'test-project-id'; + const locations: Locations = [ + { + id: 'us_central', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + { + id: 'us_east', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + ]; + const privateLocations: PrivateLocation[] = [ + { + id: 'germany', + label: 'Germany', + isServiceManaged: false, + concurrentMonitors: 1, + agentPolicyId: 'germany', + }, + ]; + const monitors = [ + { + locations: ['us_central'], + type: 'icmp', + id: 'Cloudflare-DNS', + name: 'Cloudflare DNS', + hosts: ['1.1.1.1'], + schedule: 1, + tags: ['service:smtp', 'org:google'], + privateLocations: ['Test private location 0'], + timeout: '1m', + wait: '30s', + 'service.name': 'test service', + }, + { + locations: ['us_central'], + type: 'icmp', + id: 'Cloudflare-DNS-2', + name: 'Cloudflare DNS 2', + hosts: '1.1.1.1', + schedule: 1, + tags: 'tag1,tag2', + privateLocations: ['Test private location 0'], + wait: '1m', + service: { + name: 'test service', + }, + }, + { + locations: ['us_central'], + type: 'icmp', + id: 'Cloudflare-DNS-3', + name: 'Cloudflare DNS 3', + hosts: '1.1.1.1,2.2.2.2', + schedule: 1, + tags: 'tag1,tag2', + privateLocations: ['Test private location 0'], + unsupportedKey: { + nestedUnsupportedKey: 'unnsuportedValue', + }, + }, + ]; + + it('properly normalizes http monitors', () => { + const actual = normalizeProjectMonitors({ + locations, + privateLocations, + monitors, + projectId, + namespace: 'test-space', + version: '8.5.0', + }); + expect(actual).toEqual([ + { + errors: [], + normalizedFields: { + config_id: '', + custom_heartbeat_id: 'Cloudflare-DNS-test-project-id-test-space', + enabled: true, + form_monitor_type: 'icmp', + hosts: '1.1.1.1', + journey_id: 'Cloudflare-DNS', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'Cloudflare DNS', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': 'test service', + tags: ['service:smtp', 'org:google'], + timeout: '60', + type: 'icmp', + wait: '30', + }, + unsupportedKeys: [], + }, + { + errors: [], + normalizedFields: { + config_id: '', + custom_heartbeat_id: 'Cloudflare-DNS-2-test-project-id-test-space', + enabled: true, + form_monitor_type: 'icmp', + hosts: '1.1.1.1', + journey_id: 'Cloudflare-DNS-2', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'Cloudflare DNS 2', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': 'test service', + tags: ['tag1', 'tag2'], + timeout: '16', + type: 'icmp', + wait: '60', + }, + unsupportedKeys: [], + }, + { + errors: [ + { + details: + 'Multiple hosts are not supported for icmp project monitors in 8.5.0. Please set only 1 host per monitor. You monitor was not created or updated.', + id: 'Cloudflare-DNS-3', + reason: 'Unsupported Heartbeat option', + }, + { + details: + 'The following Heartbeat options are not supported for icmp project monitors in 8.5.0: unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.', + id: 'Cloudflare-DNS-3', + reason: 'Unsupported Heartbeat option', + }, + ], + normalizedFields: { + config_id: '', + custom_heartbeat_id: 'Cloudflare-DNS-3-test-project-id-test-space', + enabled: true, + form_monitor_type: 'icmp', + hosts: '1.1.1.1', + journey_id: 'Cloudflare-DNS-3', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'Cloudflare DNS 3', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': '', + tags: ['tag1', 'tag2'], + timeout: '16', + type: 'icmp', + wait: '1', + }, + unsupportedKeys: ['unsupportedKey.nestedUnsupportedKey'], + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts index 282475f94d7c..ea4eb7dbdba8 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { getNormalizeCommonFields } from './common_fields'; -import { NormalizedProjectProps } from './browser_monitor'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { ConfigKey, @@ -14,7 +12,17 @@ import { FormMonitorType, ICMPFields, } from '../../../../common/runtime_types/monitor_management'; -import { normalizeYamlConfig, getValueInSeconds, getOptionalArrayField } from './common_fields'; +import { + NormalizerResult, + NormalizedProjectProps, + normalizeYamlConfig, + getNormalizeCommonFields, + getValueInSeconds, + getOptionalArrayField, + getOptionalListField, + getMultipleUrlsOrHostsError, + getUnsupportedKeysError, +} from './common_fields'; export const getNormalizeICMPFields = ({ locations = [], @@ -22,8 +30,10 @@ export const getNormalizeICMPFields = ({ monitor, projectId, namespace, -}: NormalizedProjectProps): { normalizedFields: ICMPFields; unsupportedKeys: string[] } => { + version, +}: NormalizedProjectProps): NormalizerResult => { const defaultFields = DEFAULT_FIELDS[DataStream.ICMP]; + const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); const commonFields = getNormalizeCommonFields({ @@ -32,8 +42,19 @@ export const getNormalizeICMPFields = ({ monitor, projectId, namespace, + version, }); + /* Check if monitor has multiple hosts */ + const hosts = getOptionalListField(monitor.hosts); + if (hosts.length > 1) { + errors.push(getMultipleUrlsOrHostsError(monitor, 'hosts', version)); + } + + if (unsupportedKeys.length) { + errors.push(getUnsupportedKeysError(monitor, unsupportedKeys, version)); + } + const normalizedFields = { ...yamlConfig, ...commonFields, @@ -41,9 +62,6 @@ export const getNormalizeICMPFields = ({ [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.ICMP, [ConfigKey.HOSTS]: getOptionalArrayField(monitor[ConfigKey.HOSTS]) || defaultFields[ConfigKey.HOSTS], - [ConfigKey.TIMEOUT]: monitor.timeout - ? getValueInSeconds(monitor.timeout) - : defaultFields[ConfigKey.TIMEOUT], [ConfigKey.WAIT]: monitor.wait ? getValueInSeconds(monitor.wait) || defaultFields[ConfigKey.WAIT] : defaultFields[ConfigKey.WAIT], @@ -54,5 +72,6 @@ export const getNormalizeICMPFields = ({ ...normalizedFields, }, unsupportedKeys, + errors, }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts index 82b2acfacbf5..6bac17c0fb54 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts @@ -14,14 +14,7 @@ import { getNormalizeBrowserFields } from './browser_monitor'; import { getNormalizeICMPFields } from './icmp_monitor'; import { getNormalizeTCPFields } from './tcp_monitor'; import { getNormalizeHTTPFields } from './http_monitor'; - -export interface NormalizedProjectProps { - locations: Locations; - privateLocations: PrivateLocation[]; - monitor: ProjectMonitor; - projectId: string; - namespace: string; -} +import { NormalizedProjectProps } from './common_fields'; export const normalizeProjectMonitor = (props: NormalizedProjectProps) => { const { monitor } = props; @@ -50,14 +43,23 @@ export const normalizeProjectMonitors = ({ monitors = [], projectId, namespace, + version, }: { locations: Locations; privateLocations: PrivateLocation[]; monitors: ProjectMonitor[]; projectId: string; namespace: string; + version: string; }) => { return monitors.map((monitor) => { - return normalizeProjectMonitor({ monitor, locations, privateLocations, projectId, namespace }); + return normalizeProjectMonitor({ + monitor, + locations, + privateLocations, + projectId, + namespace, + version, + }); }); }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts new file mode 100644 index 000000000000..094bf018ba12 --- /dev/null +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts @@ -0,0 +1,265 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { Locations, LocationStatus, PrivateLocation } from '../../../../common/runtime_types'; +import { normalizeProjectMonitors } from '.'; + +describe('http normalizers', () => { + describe('normalize push monitors', () => { + const projectId = 'test-project-id'; + const locations: Locations = [ + { + id: 'us_central', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + { + id: 'us_east', + label: 'Test Location', + geo: { lat: 33.333, lon: 73.333 }, + url: 'test-url', + isServiceManaged: true, + status: LocationStatus.GA, + }, + ]; + const privateLocations: PrivateLocation[] = [ + { + id: 'germany', + label: 'Germany', + isServiceManaged: false, + concurrentMonitors: 1, + agentPolicyId: 'germany', + }, + ]; + const monitors = [ + { + locations: ['us_central'], + type: 'tcp', + id: 'gmail-smtp', + name: 'GMail SMTP', + hosts: ['smtp.gmail.com:587'], + schedule: 1, + tags: ['service:smtp', 'org:google'], + privateLocations: ['BEEP'], + 'service.name': 'test service', + 'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'], + }, + { + locations: ['us_central'], + type: 'tcp', + id: 'always-down', + name: 'Always Down', + hosts: 'localhost:18278', + schedule: 1, + tags: 'tag1,tag2', + privateLocations: ['BEEP'], + service: { + name: 'test service', + }, + ssl: { + supported_protocols: 'TLSv1.2,TLSv1.3', + }, + }, + { + locations: ['us_central'], + type: 'tcp', + id: 'always-down', + name: 'Always Down', + hosts: ['localhost', 'anotherhost'], + ports: ['5698'], + schedule: 1, + tags: 'tag1,tag2', + privateLocations: ['BEEP'], + unsupportedKey: { + nestedUnsupportedKey: 'unnsuportedValue', + }, + }, + ]; + + it('properly normalizes http monitors', () => { + const actual = normalizeProjectMonitors({ + locations, + privateLocations, + monitors, + projectId, + namespace: 'test-space', + version: '8.5.0', + }); + expect(actual).toEqual([ + { + errors: [], + normalizedFields: { + __ui: { + is_tls_enabled: false, + }, + 'check.receive': '', + 'check.send': '', + config_id: '', + custom_heartbeat_id: 'gmail-smtp-test-project-id-test-space', + enabled: true, + form_monitor_type: 'tcp', + hosts: 'smtp.gmail.com:587', + journey_id: 'gmail-smtp', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'GMail SMTP', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + proxy_url: '', + proxy_use_local_resolver: false, + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': 'test service', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: ['service:smtp', 'org:google'], + timeout: '16', + type: 'tcp', + }, + unsupportedKeys: [], + }, + { + errors: [], + normalizedFields: { + __ui: { + is_tls_enabled: false, + }, + 'check.receive': '', + 'check.send': '', + config_id: '', + custom_heartbeat_id: 'always-down-test-project-id-test-space', + enabled: true, + form_monitor_type: 'tcp', + hosts: 'localhost:18278', + journey_id: 'always-down', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'Always Down', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + proxy_url: '', + proxy_use_local_resolver: false, + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': 'test service', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'ssl.supported_protocols': ['TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: ['tag1', 'tag2'], + timeout: '16', + type: 'tcp', + }, + unsupportedKeys: [], + }, + { + errors: [ + { + details: + 'Multiple hosts are not supported for tcp project monitors in 8.5.0. Please set only 1 host per monitor. You monitor was not created or updated.', + id: 'always-down', + reason: 'Unsupported Heartbeat option', + }, + { + details: + 'The following Heartbeat options are not supported for tcp project monitors in 8.5.0: ports|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.', + id: 'always-down', + reason: 'Unsupported Heartbeat option', + }, + ], + normalizedFields: { + __ui: { + is_tls_enabled: false, + }, + 'check.receive': '', + 'check.send': '', + config_id: '', + custom_heartbeat_id: 'always-down-test-project-id-test-space', + enabled: true, + form_monitor_type: 'tcp', + hosts: 'localhost', + journey_id: 'always-down', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + status: 'ga', + url: 'test-url', + }, + ], + name: 'Always Down', + namespace: 'test_space', + origin: 'project', + original_space: 'test-space', + project_id: 'test-project-id', + proxy_url: '', + proxy_use_local_resolver: false, + schedule: { + number: '1', + unit: 'm', + }, + 'service.name': '', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: ['tag1', 'tag2'], + timeout: '16', + type: 'tcp', + }, + unsupportedKeys: ['ports', 'unsupportedKey.nestedUnsupportedKey'], + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts index 8a85b2959d80..1045bc5ebff7 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts @@ -4,18 +4,25 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { NormalizedProjectProps } from './browser_monitor'; +import { get } from 'lodash'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; -import { normalizeYamlConfig, getValueInSeconds } from './common_fields'; - import { ConfigKey, DataStream, FormMonitorType, TCPFields, + TLSVersion, } from '../../../../common/runtime_types/monitor_management'; -import { getNormalizeCommonFields, getOptionalArrayField } from './common_fields'; +import { + NormalizedProjectProps, + NormalizerResult, + normalizeYamlConfig, + getNormalizeCommonFields, + getOptionalArrayField, + getOptionalListField, + getMultipleUrlsOrHostsError, + getUnsupportedKeysError, +} from './common_fields'; export const getNormalizeTCPFields = ({ locations = [], @@ -23,8 +30,10 @@ export const getNormalizeTCPFields = ({ monitor, projectId, namespace, -}: NormalizedProjectProps): { normalizedFields: TCPFields; unsupportedKeys: string[] } => { + version, +}: NormalizedProjectProps): NormalizerResult => { const defaultFields = DEFAULT_FIELDS[DataStream.TCP]; + const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); const commonFields = getNormalizeCommonFields({ @@ -33,8 +42,19 @@ export const getNormalizeTCPFields = ({ monitor, projectId, namespace, + version, }); + /* Check if monitor has multiple hosts */ + const hosts = getOptionalListField(monitor.hosts); + if (hosts.length > 1) { + errors.push(getMultipleUrlsOrHostsError(monitor, 'hosts', version)); + } + + if (unsupportedKeys.length) { + errors.push(getUnsupportedKeysError(monitor, unsupportedKeys, version)); + } + const normalizedFields = { ...yamlConfig, ...commonFields, @@ -42,9 +62,9 @@ export const getNormalizeTCPFields = ({ [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, [ConfigKey.HOSTS]: getOptionalArrayField(monitor[ConfigKey.HOSTS]) || defaultFields[ConfigKey.HOSTS], - [ConfigKey.TIMEOUT]: monitor.timeout - ? getValueInSeconds(monitor.timeout) - : defaultFields[ConfigKey.TIMEOUT], + [ConfigKey.TLS_VERSION]: get(monitor, ConfigKey.TLS_VERSION) + ? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[]) + : defaultFields[ConfigKey.TLS_VERSION], }; return { normalizedFields: { @@ -52,5 +72,6 @@ export const getNormalizeTCPFields = ({ ...normalizedFields, }, unsupportedKeys, + errors, }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts index aa0be87f0e81..1ca27869aa2c 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts @@ -197,22 +197,17 @@ export class ProjectMonitorFormatter { try { await this.validatePermissions({ monitor }); - const { normalizedFields: normalizedMonitor, unsupportedKeys } = normalizeProjectMonitor({ + const { normalizedFields: normalizedMonitor, errors } = normalizeProjectMonitor({ monitor, locations: this.locations, privateLocations: this.privateLocations, projectId: this.projectId, namespace: this.spaceId, + version: this.server.kibanaVersion, }); - if (unsupportedKeys.length) { - this.failedMonitors.push({ - id: monitor.id, - reason: 'Unsupported Heartbeat option', - details: `The following Heartbeat options are not supported for ${ - monitor.type - } project monitors in ${this.server.kibanaVersion}: ${unsupportedKeys.join('|')}`, - }); + if (errors.length) { + this.failedMonitors.push(...errors); this.handleStreamingMessage({ message: `${monitor.id}: failed to create or update monitor`, }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts index 1110bbb875c7..105e6521c1da 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts @@ -112,7 +112,12 @@ export default function ({ getService }: FtrProviderContext) { expect(messages[2].failedMonitors).eql([ { id: httpProjectMonitors.monitors[0].id, - details: `The following Heartbeat options are not supported for ${httpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: check.response.body|unsupportedKey.nestedUnsupportedKey`, + details: `Multiple urls are not supported for http project monitors in ${kibanaVersion}. Please set only 1 url per monitor. You monitor was not created or updated.`, + reason: 'Unsupported Heartbeat option', + }, + { + id: httpProjectMonitors.monitors[0].id, + details: `The following Heartbeat options are not supported for ${httpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: check.response.body|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`, reason: 'Unsupported Heartbeat option', }, ]); @@ -200,7 +205,12 @@ export default function ({ getService }: FtrProviderContext) { expect(messages[2].failedMonitors).eql([ { id: tcpProjectMonitors.monitors[2].id, - details: `The following Heartbeat options are not supported for ${tcpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: ports|unsupportedKey.nestedUnsupportedKey`, + details: `Multiple hosts are not supported for tcp project monitors in ${kibanaVersion}. Please set only 1 host per monitor. You monitor was not created or updated.`, + reason: 'Unsupported Heartbeat option', + }, + { + id: tcpProjectMonitors.monitors[2].id, + details: `The following Heartbeat options are not supported for ${tcpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: ports|unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`, reason: 'Unsupported Heartbeat option', }, ]); @@ -284,7 +294,12 @@ export default function ({ getService }: FtrProviderContext) { expect(messages[2].failedMonitors).eql([ { id: icmpProjectMonitors.monitors[2].id, - details: `The following Heartbeat options are not supported for ${icmpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: unsupportedKey.nestedUnsupportedKey`, + details: `Multiple hosts are not supported for icmp project monitors in ${kibanaVersion}. Please set only 1 host per monitor. You monitor was not created or updated.`, + reason: 'Unsupported Heartbeat option', + }, + { + id: icmpProjectMonitors.monitors[2].id, + details: `The following Heartbeat options are not supported for ${icmpProjectMonitors.monitors[0].type} project monitors in ${kibanaVersion}: unsupportedKey.nestedUnsupportedKey. You monitor was not created or updated.`, reason: 'Unsupported Heartbeat option', }, ]); diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json index fc754cbdcd03..42044c8ba9cf 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json @@ -9,7 +9,8 @@ "id": "my-monitor-2", "name": "My Monitor 2", "urls": [ - "http://localhost:9200" + "http://localhost:9200", + "http://anotherurl:9200" ], "schedule": 60, "timeout": "80s", @@ -32,7 +33,6 @@ "saved" ] }, - "content": "", "unsupportedKey": { "nestedUnsupportedKey": "unsupportedValue" } @@ -69,8 +69,7 @@ "saved" ] } - }, - "content": "" + } } ] } \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_icmp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_icmp_monitor.json index 8dec1b28d50c..b19e91088258 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_icmp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_icmp_monitor.json @@ -14,7 +14,6 @@ "schedule": 1, "tags": [ "service:smtp", "org:google" ], "privateLocations": [ "Test private location 0" ], - "content": "", "wait": "30s" }, { @@ -26,7 +25,6 @@ "schedule": 1, "tags": "tag1,tag2", "privateLocations": [ "Test private location 0" ], - "content": "", "wait": "1m" }, { @@ -34,11 +32,10 @@ "type": "icmp", "id": "Cloudflare-DNS-3", "name": "Cloudflare DNS 3", - "hosts": "1.1.1.1", + "hosts": "1.1.1.1,2.2.2.2", "schedule": 1, "tags": "tag1,tag2", "privateLocations": [ "Test private location 0" ], - "content": "", "unsupportedKey": { "nestedUnsupportedKey": "unnsuportedValue" } diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_tcp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_tcp_monitor.json index 30061673079d..82d6c8c557e7 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_tcp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_tcp_monitor.json @@ -10,8 +10,7 @@ "hosts": [ "smtp.gmail.com:587" ], "schedule": 1, "tags": [ "service:smtp", "org:google" ], - "privateLocations": [ "BEEP" ], - "content": "" + "privateLocations": [ "BEEP" ] }, { "locations": [ "localhost" ], @@ -21,20 +20,18 @@ "hosts": "localhost:18278", "schedule": 1, "tags": "tag1,tag2", - "privateLocations": [ "BEEP" ], - "content": "" + "privateLocations": [ "BEEP" ] }, { "locations": [ "localhost" ], "type": "tcp", "id": "always-down", "name": "Always Down", - "hosts": "localhost", + "hosts": ["localhost", "anotherhost"], "ports": ["5698"], "schedule": 1, "tags": "tag1,tag2", "privateLocations": [ "BEEP" ], - "content": "", "unsupportedKey": { "nestedUnsupportedKey": "unnsuportedValue" } From e0b486051a7b360301e85c55fed995fc64b8273c Mon Sep 17 00:00:00 2001 From: JD Kurma Date: Wed, 28 Sep 2022 18:02:47 -0400 Subject: [PATCH 089/185] fix manifest url + more descriptive name (#142158) --- .../plugins/security_solution/server/lib/telemetry/artifact.ts | 2 +- .../server/lib/telemetry/tasks/configuration.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/artifact.ts b/x-pack/plugins/security_solution/server/lib/telemetry/artifact.ts index 46d0d6e665c4..07ec2b6f2e49 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/artifact.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/artifact.ts @@ -26,7 +26,7 @@ class Artifact implements IArtifact { this.receiver = receiver; this.esClusterInfo = await this.receiver.fetchClusterInfo(); const version = this.esClusterInfo?.version?.number; - this.manifestUrl = `${this.CDN_URL}/downloads/endpoint/manifest/artifacts-${version}.zip`; + this.manifestUrl = `${this.CDN_URL}/downloads/kibana/manifest/artifacts-${version}.zip`; } public async getArtifact(name: string): Promise { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/configuration.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/configuration.ts index f664fccbf75d..d266d2f1c769 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/configuration.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/configuration.ts @@ -29,7 +29,7 @@ export function createTelemetryConfigurationTaskConfig() { taskExecutionPeriod: TaskExecutionPeriod ) => { try { - const artifactName = 'telemetry-configuration-v1'; + const artifactName = 'telemetry-buffer-and-batch-sizes-v1'; const configArtifact = (await artifactService.getArtifact( artifactName )) as unknown as TelemetryConfiguration; From c71ab04eb577b935d80bf15e5afbb9294c799264 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Wed, 28 Sep 2022 18:26:49 -0400 Subject: [PATCH 090/185] [APM] Fixes service count API and modal scroll (#141725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [APM] Fixes multiple parallel search calls for service count to one call with a filter agg (#141242) * updates the description text when saving a group * fixes scrolling issue in save group modal * adds descripion for service count time range in service group list * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Add comment about only aggregating over metrics documents. * Update x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts PR feedback Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../service_group_save/select_services.tsx | 134 +++++++++--------- .../service_list_preview.tsx | 2 +- .../service_groups_list/index.tsx | 6 + .../service_groups/get_services_counts.ts | 56 +++++--- .../apm/server/routes/service_groups/route.ts | 43 ++---- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 8 files changed, 120 insertions(+), 124 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/select_services.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/select_services.tsx index 251fa03735f1..7a97583bbd4a 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/select_services.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/select_services.tsx @@ -36,7 +36,7 @@ const CentralizedContainer = styled.div` `; const MAX_CONTAINER_HEIGHT = 600; -const MODAL_HEADER_HEIGHT = 122; +const MODAL_HEADER_HEIGHT = 180; const MODAL_FOOTER_HEIGHT = 80; const suggestedFieldsWhitelist = [ @@ -118,10 +118,73 @@ export function SelectServices({ 'xpack.apm.serviceGroups.selectServicesForm.subtitle', { defaultMessage: - 'Use a query to select services for this group. Services that match this query within the last 24 hours will be assigned to the group.', + 'Use a query to select services for this group. The preview shows services that match this query within the last 24 hours.', } )} + + + { + setKuery(value); + }} + onChange={(value) => { + setStagedKuery(value); + }} + value={kuery} + suggestionFilter={(querySuggestion) => { + if ('field' in querySuggestion) { + const { + field: { + spec: { name: fieldName }, + }, + } = querySuggestion; + + return ( + fieldName.startsWith('label') || + suggestedFieldsWhitelist.includes(fieldName) + ); + } + return true; + }} + /> + + + { + setKuery(stagedKuery); + }} + iconType={!kuery ? 'search' : 'refresh'} + isDisabled={isServiceListPreviewLoading || !stagedKuery} + > + {!kuery + ? i18n.translate( + 'xpack.apm.serviceGroups.selectServicesForm.preview', + { defaultMessage: 'Preview' } + ) + : i18n.translate( + 'xpack.apm.serviceGroups.selectServicesForm.refresh', + { defaultMessage: 'Refresh' } + )} + + + + {kuery && data?.items && ( + + {i18n.translate( + 'xpack.apm.serviceGroups.selectServicesForm.matchingServiceCount', + { + defaultMessage: + '{servicesCount} {servicesCount, plural, =0 {services} one {service} other {services}} match the query', + values: { servicesCount: data?.items.length }, + } + )} + + )} - - - - { - setKuery(value); - }} - onChange={(value) => { - setStagedKuery(value); - }} - value={kuery} - suggestionFilter={(querySuggestion) => { - if ('field' in querySuggestion) { - const { - field: { - spec: { name: fieldName }, - }, - } = querySuggestion; - - return ( - fieldName.startsWith('label') || - suggestedFieldsWhitelist.includes(fieldName) - ); - } - return true; - }} - /> - - - { - setKuery(stagedKuery); - }} - iconType={!kuery ? 'search' : 'refresh'} - isDisabled={isServiceListPreviewLoading || !stagedKuery} - > - {!kuery - ? i18n.translate( - 'xpack.apm.serviceGroups.selectServicesForm.preview', - { defaultMessage: 'Preview' } - ) - : i18n.translate( - 'xpack.apm.serviceGroups.selectServicesForm.refresh', - { defaultMessage: 'Refresh' } - )} - - - - - {kuery && data?.items && ( - - - {i18n.translate( - 'xpack.apm.serviceGroups.selectServicesForm.matchingServiceCount', - { - defaultMessage: - '{servicesCount} {servicesCount, plural, =0 {services} one {service} other {services}} match the query', - values: { servicesCount: data?.items.length }, - } - )} - - - )} {!kuery && ( diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/service_list_preview.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/service_list_preview.tsx index eb366b84ceef..66105c106805 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/service_list_preview.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/service_list_preview.tsx @@ -37,7 +37,7 @@ type SORT_FIELD = 'serviceName' | 'environments' | 'agentName'; export function ServiceListPreview({ items, isLoading }: Props) { const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(5); + const [pageSize, setPageSize] = useState(10); const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); const [sortDirection, setSortDirection] = useState( DEFAULT_SORT_DIRECTION diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx index d0bf3a9f24b7..734298dabe9e 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx @@ -156,6 +156,12 @@ export function ServiceGroupsList() { + + {i18n.translate('xpack.apm.serviceGroups.listDescription', { + defaultMessage: + 'Displayed service counts reflect the last 24 hours.', + })} + {items.length ? ( diff --git a/x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts b/x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts index 880e3d248889..c820cdd8445f 100644 --- a/x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts +++ b/x-pack/plugins/apm/server/routes/service_groups/get_services_counts.ts @@ -7,50 +7,72 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { Setup } from '../../lib/helpers/setup_request'; import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { SavedServiceGroup } from '../../../common/service_groups'; export async function getServicesCounts({ setup, - kuery, - maxNumberOfServices, start, end, + serviceGroups, }: { setup: Setup; - kuery: string; - maxNumberOfServices: number; start: number; end: number; + serviceGroups: SavedServiceGroup[]; }) { const { apmEventClient } = setup; - const response = await apmEventClient.search('get_services_count', { + const serviceGroupsKueryMap: Record = + serviceGroups.reduce((acc, sg) => { + return { + ...acc, + [sg.id]: kqlQuery(sg.kuery)[0], + }; + }, {}); + + const params = { apm: { - events: [ - ProcessorEvent.metric, - ProcessorEvent.transaction, - ProcessorEvent.span, - ProcessorEvent.error, - ], + // We're limiting the service count to only metrics documents. If a user + // actively disables system/app metrics and a service only ingests error + // events, that service will not be included in the service groups count. + // This is an edge case that only effects the count preview label. + events: [ProcessorEvent.metric], }, body: { track_total_hits: 0, size: 0, query: { bool: { - filter: [...rangeQuery(start, end), ...kqlQuery(kuery)], + filter: rangeQuery(start, end), }, }, aggs: { - services_count: { - cardinality: { - field: SERVICE_NAME, + service_groups: { + filters: { + filters: serviceGroupsKueryMap, + }, + aggs: { + services_count: { + cardinality: { + field: SERVICE_NAME, + }, + }, }, }, }, }, - }); + }; + const response = await apmEventClient.search('get_services_count', params); - return response?.aggregations?.services_count.value ?? 0; + const buckets: Record = + response?.aggregations?.service_groups.buckets ?? {}; + return Object.keys(buckets).reduce((acc, key) => { + return { + ...acc, + [key]: buckets[key].services_count.value, + }; + }, {}); } diff --git a/x-pack/plugins/apm/server/routes/service_groups/route.ts b/x-pack/plugins/apm/server/routes/service_groups/route.ts index ff37dc1f1c16..4430aaae760e 100644 --- a/x-pack/plugins/apm/server/routes/service_groups/route.ts +++ b/x-pack/plugins/apm/server/routes/service_groups/route.ts @@ -7,7 +7,6 @@ import * as t from 'io-ts'; import { apmServiceGroupMaxNumberOfServices } from '@kbn/observability-plugin/common'; -import { keyBy, mapValues } from 'lodash'; import { setupRequest } from '../../lib/helpers/setup_request'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { kueryRt, rangeRt } from '../default_api_types'; @@ -52,50 +51,26 @@ const serviceGroupsWithServiceCountRoute = createApmServerRoute({ const { context, params } = resources; const { savedObjects: { client: savedObjectsClient }, - uiSettings: { client: uiSettingsClient }, } = await context.core; const { query: { start, end }, } = params; - const [setup, maxNumberOfServices] = await Promise.all([ - setupRequest(resources), - uiSettingsClient.get(apmServiceGroupMaxNumberOfServices), - ]); + const setup = await setupRequest(resources); const serviceGroups = await getServiceGroups({ savedObjectsClient, }); - const serviceGroupsWithServiceCount = await Promise.all( - serviceGroups.map( - async ({ - id, - kuery, - }): Promise<{ id: string; servicesCount: number }> => { - const servicesCount = await getServicesCounts({ - setup, - kuery, - maxNumberOfServices, - start, - end, - }); - - return { - id, - servicesCount, - }; - } - ) - ); - - const servicesCounts = mapValues( - keyBy(serviceGroupsWithServiceCount, 'id'), - 'servicesCount' - ); - - return { servicesCounts }; + return { + servicesCounts: await getServicesCounts({ + setup, + serviceGroups, + start, + end, + }), + }; }, }); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 612838bf5ea5..e4db41419cb9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -7542,7 +7542,6 @@ "xpack.apm.serviceGroups.selectServicesForm.preview": "Aperçu", "xpack.apm.serviceGroups.selectServicesForm.refresh": "Actualiser", "xpack.apm.serviceGroups.selectServicesForm.saveGroup": "Enregistrer le groupe", - "xpack.apm.serviceGroups.selectServicesForm.subtitle": "Utilisez une requête pour sélectionner les services pour ce groupe. Les services qui correspondent à cette requête dans les dernières 24 heures seront affectés au groupe.", "xpack.apm.serviceGroups.selectServicesForm.title": "Sélectionner des services", "xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "Environnements", "xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "Nom", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1ace6b0a7665..557f4014f1e4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7529,7 +7529,6 @@ "xpack.apm.serviceGroups.selectServicesForm.preview": "プレビュー", "xpack.apm.serviceGroups.selectServicesForm.refresh": "更新", "xpack.apm.serviceGroups.selectServicesForm.saveGroup": "グループを保存", - "xpack.apm.serviceGroups.selectServicesForm.subtitle": "クエリを使用してこのグループのサービスを選択します。過去24時間以内にこのクエリと一致したサービスはグループに割り当てられます。", "xpack.apm.serviceGroups.selectServicesForm.title": "サービスを選択", "xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "環境", "xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "名前", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2cdc7fa51626..7fb05ec21f71 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7546,7 +7546,6 @@ "xpack.apm.serviceGroups.selectServicesForm.preview": "预览", "xpack.apm.serviceGroups.selectServicesForm.refresh": "刷新", "xpack.apm.serviceGroups.selectServicesForm.saveGroup": "保存组", - "xpack.apm.serviceGroups.selectServicesForm.subtitle": "使用查询为该组选择服务。过去 24 小时内与此查询匹配的服务将分配给该组。", "xpack.apm.serviceGroups.selectServicesForm.title": "选择服务", "xpack.apm.serviceGroups.selectServicesList.environmentColumnLabel": "环境", "xpack.apm.serviceGroups.selectServicesList.nameColumnLabel": "名称", From 92b686a76377e8dc264c0550d278fa134300dfd1 Mon Sep 17 00:00:00 2001 From: Klim Markelov Date: Thu, 29 Sep 2022 00:48:49 +0200 Subject: [PATCH 091/185] Change back the analytics js file url in integration tab (#142114) --- .../analytics_collection_integrate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate.tsx index 47f0f161263a..36b527097a30 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate.tsx @@ -49,7 +49,7 @@ export const AnalyticsCollectionIntegrate: React.FC From 07495a440774c5bc8d7af0a0310d4655139952f9 Mon Sep 17 00:00:00 2001 From: Karl Godard Date: Wed, 28 Sep 2022 18:00:20 -0700 Subject: [PATCH 092/185] fixes a few minor issues with playback in xtermjs (#142172) Co-authored-by: Karl Godard --- .../components/tty_player/hooks.test.tsx | 15 +++++++++++ .../public/components/tty_player/hooks.ts | 5 ++-- .../public/components/tty_player/index.tsx | 7 ++++- .../components/tty_search_bar/index.test.tsx | 4 +++ .../components/tty_search_bar/index.tsx | 27 ++++++++++++++----- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx b/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx index 004bd5bc757e..8b2161c3b121 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx @@ -167,6 +167,21 @@ describe('TTYPlayer/hooks', () => { expect(result.current.currentLine).toBe(initialProps.lines.length - 1); }); + it('should not print the first line twice after playback starts', async () => { + const { result, rerender } = renderHook((props) => useXtermPlayer(props), { + initialProps, + }); + + rerender({ ...initialProps, isPlaying: true }); + act(() => { + // advance render loop + jest.advanceTimersByTime(DEFAULT_TTY_PLAYSPEED_MS); + }); + rerender({ ...initialProps, isPlaying: false }); + + expect(result.current.terminal.buffer.active.getLine(0)?.translateToString(true)).toBe('256'); + }); + it('will allow a plain text search highlight on the last line printed', async () => { const { result: xTermResult } = renderHook((props) => useXtermPlayer(props), { initialProps, diff --git a/x-pack/plugins/session_view/public/components/tty_player/hooks.ts b/x-pack/plugins/session_view/public/components/tty_player/hooks.ts index 08f163903b0a..680d50283d5f 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/hooks.ts +++ b/x-pack/plugins/session_view/public/components/tty_player/hooks.ts @@ -281,13 +281,12 @@ export const useXtermPlayer = ({ useEffect(() => { if (isPlaying) { const timer = setTimeout(() => { - render(currentLine, false); - - if (currentLine === lines.length - 1) { + if (!hasNextPage && currentLine === lines.length - 1) { setIsPlaying(false); } else { const nextLine = Math.min(lines.length - 1, currentLine + TTY_LINES_PER_FRAME); setCurrentLine(nextLine); + render(nextLine, false); } }, playSpeed); diff --git a/x-pack/plugins/session_view/public/components/tty_player/index.tsx b/x-pack/plugins/session_view/public/components/tty_player/index.tsx index ba57fc9d845c..cb2746736c02 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/index.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/index.tsx @@ -134,7 +134,12 @@ export const TTYPlayer = ({ - + diff --git a/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx b/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx index 2f952b48e9d5..06fa17a6c151 100644 --- a/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx @@ -34,6 +34,7 @@ describe('TTYSearchBar component', () => { lines, seekToLine: jest.fn(), xTermSearchFn: jest.fn(), + setIsPlaying: jest.fn(), }; }); @@ -59,6 +60,7 @@ describe('TTYSearchBar component', () => { expect(props.xTermSearchFn).toHaveBeenCalledTimes(2); expect(props.xTermSearchFn).toHaveBeenNthCalledWith(1, '', 0); expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '-h', 6); + expect(props.setIsPlaying).toHaveBeenCalledWith(false); }); it('calls seekToline and xTermSearchFn when currentMatch changes', async () => { @@ -85,6 +87,7 @@ describe('TTYSearchBar component', () => { expect(props.xTermSearchFn).toHaveBeenNthCalledWith(1, '', 0); expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '-h', 6); expect(props.xTermSearchFn).toHaveBeenNthCalledWith(3, '-h', 13); + expect(props.setIsPlaying).toHaveBeenCalledTimes(3); }); it('calls xTermSearchFn with empty query when search is cleared', async () => { @@ -101,5 +104,6 @@ describe('TTYSearchBar component', () => { await new Promise((r) => setTimeout(r, 100)); expect(props.xTermSearchFn).toHaveBeenNthCalledWith(3, '', 0); + expect(props.setIsPlaying).toHaveBeenCalledWith(false); }); }); diff --git a/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx b/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx index e41b3b967cfa..18b829127ab2 100644 --- a/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx +++ b/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx @@ -19,15 +19,24 @@ export interface TTYSearchBarDeps { lines: IOLine[]; seekToLine(index: number): void; xTermSearchFn(query: string, index: number): void; + setIsPlaying(value: boolean): void; } -export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarDeps) => { +const STRIP_NEWLINES_REGEX = /^(\r\n|\r|\n|\n\r)/; + +export const TTYSearchBar = ({ + lines, + seekToLine, + xTermSearchFn, + setIsPlaying, +}: TTYSearchBarDeps) => { const [currentMatch, setCurrentMatch] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const jumpToMatch = useCallback( (match) => { if (match) { + setIsPlaying(false); const goToLine = lines.indexOf(match.line); seekToLine(goToLine); } @@ -40,7 +49,7 @@ export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarD clearTimeout(timeout); }; }, - [lines, seekToLine, xTermSearchFn, searchQuery] + [setIsPlaying, lines, seekToLine, xTermSearchFn, searchQuery] ); const searchResults = useMemo(() => { @@ -53,7 +62,7 @@ export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarD const cursorMovement = current.value.match(/^\x1b\[\d+;(\d+)(H|d)/); const regex = new RegExp(searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'ig'); const lineMatches = stripAnsi(current.value) - .replace(/^\r|\r?\n/, '') + .replace(STRIP_NEWLINES_REGEX, '') .matchAll(regex); if (lineMatches) { @@ -90,10 +99,14 @@ export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarD return matches; }, [searchQuery, lines, jumpToMatch, xTermSearchFn]); - const onSearch = useCallback((query) => { - setSearchQuery(query); - setCurrentMatch(null); - }, []); + const onSearch = useCallback( + (query) => { + setIsPlaying(false); + setSearchQuery(query); + setCurrentMatch(null); + }, + [setIsPlaying] + ); const onSetCurrentMatch = useCallback( (index) => { From a192d34a524cfd25d2aa1bf366dcb82c5b2c0424 Mon Sep 17 00:00:00 2001 From: Luke Gmys Date: Thu, 29 Sep 2022 06:10:55 +0200 Subject: [PATCH 093/185] [TIP] Extract useFilters logic to a shared context (#141766) --- .../mocks/mock_indicators_filters_context.tsx | 10 +- .../public/common/mocks/story_providers.tsx | 2 +- .../public/common/mocks/test_providers.tsx | 39 +++--- .../fields_table/fields_table.stories.tsx | 2 +- .../components/flyout/flyout.stories.tsx | 2 +- .../overview_tab/block/block.stories.tsx | 2 +- .../overview_tab/overview_tab.stories.tsx | 2 +- .../flyout/table_tab/table_tab.stories.tsx | 2 +- .../indicators_barchart_wrapper.stories.tsx | 11 +- .../indicators_barchart_wrapper.test.tsx | 10 +- .../indicators_barchart_wrapper.tsx | 19 +-- .../indicators_table.stories.tsx | 2 +- .../indicators_filters}/context.ts | 13 +- .../containers/indicators_filters/index.ts | 10 ++ .../indicators_filters/indicators_filters.tsx | 111 +++++++++++++++--- .../hooks/use_aggregated_indicators.test.tsx | 46 ++++---- .../hooks/use_aggregated_indicators.ts | 14 ++- .../indicators/hooks/use_indicators.test.tsx | 21 ++++ .../indicators/hooks/use_indicators.ts | 7 +- .../hooks/use_indicators_filters_context.ts | 5 +- .../indicators/indicators_page.test.tsx | 2 +- .../modules/indicators/indicators_page.tsx | 82 ++++++++----- .../filter_in/filter_in.stories.tsx | 2 +- .../filter_out/filter_out.stories.tsx | 2 +- .../hooks/use_filters/use_filters.test.tsx | 31 +++-- .../hooks/use_filters/use_filters.ts | 108 ++--------------- 26 files changed, 329 insertions(+), 228 deletions(-) rename x-pack/plugins/threat_intelligence/public/modules/indicators/{ => containers/indicators_filters}/context.ts (55%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/index.ts diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx index 6e9c0b373a0f..4b7832631fd1 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx @@ -6,11 +6,19 @@ */ import { FilterManager } from '@kbn/data-plugin/public'; -import { IndicatorsFiltersContextValue } from '../../modules/indicators/context'; +import { IndicatorsFiltersContextValue } from '../../modules/indicators/containers/indicators_filters/context'; export const mockIndicatorsFiltersContext: IndicatorsFiltersContextValue = { filterManager: { getFilters: () => [], setFilters: () => {}, } as unknown as FilterManager, + filters: [], + filterQuery: { + language: 'kuery', + query: '', + }, + handleSavedQuery: () => {}, + handleSubmitQuery: () => {}, + handleSubmitTimeRange: () => {}, }; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx index eea2596327fb..7cea653c45e5 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx @@ -16,7 +16,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { mockIndicatorsFiltersContext } from './mock_indicators_filters_context'; import { SecuritySolutionContext } from '../../containers/security_solution_context'; import { getSecuritySolutionContextMock } from './mock_security_context'; -import { IndicatorsFiltersContext } from '../../modules/indicators/context'; +import { IndicatorsFiltersContext } from '../../modules/indicators/containers/indicators_filters/context'; import { FieldTypesContext } from '../../containers/field_types_provider'; import { generateFieldTypeMap } from './mock_field_type_map'; import { mockUiSettingsService } from './mock_kibana_ui_settings_service'; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx index a93b6bfe3046..c41f8972fd60 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx @@ -18,12 +18,13 @@ import { createTGridMocks } from '@kbn/timelines-plugin/public/mock'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { MemoryRouter } from 'react-router-dom'; import { KibanaContext } from '../../hooks/use_kibana'; import { SecuritySolutionPluginContext } from '../../types'; import { getSecuritySolutionContextMock } from './mock_security_context'; import { mockUiSetting } from './mock_kibana_ui_settings_service'; import { SecuritySolutionContext } from '../../containers/security_solution_context'; -import { IndicatorsFiltersContext } from '../../modules/indicators/context'; +import { IndicatorsFiltersContext } from '../../modules/indicators/containers/indicators_filters/context'; import { mockIndicatorsFiltersContext } from './mock_indicators_filters_context'; import { FieldTypesContext } from '../../containers/field_types_provider'; import { generateFieldTypeMap } from './mock_field_type_map'; @@ -128,23 +129,25 @@ export const mockedServices = { }; export const TestProvidersComponent: FC = ({ children }) => ( - - - - - - - - - {children} - - - - - - - - + + + + + + + + + + {children} + + + + + + + + + ); export type MockedSearch = jest.Mocked; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx index 8d3f13ae01af..eb0ed8fb045e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.stories.tsx @@ -10,7 +10,7 @@ import { mockIndicatorsFiltersContext } from '../../../../../common/mocks/mock_i import { IndicatorFieldsTable } from '.'; import { generateMockIndicator } from '../../../../../../common/types/indicator'; import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; -import { IndicatorsFiltersContext } from '../../../context'; +import { IndicatorsFiltersContext } from '../../../containers/indicators_filters'; export default { component: IndicatorFieldsTable, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx index 12345056c7a9..69236e778178 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.stories.tsx @@ -14,7 +14,7 @@ import { mockUiSettingsService } from '../../../../common/mocks/mock_kibana_ui_s import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { IndicatorsFlyout } from '.'; -import { IndicatorsFiltersContext } from '../../context'; +import { IndicatorsFiltersContext } from '../../containers/indicators_filters'; export default { component: IndicatorsFlyout, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx index 1049518f4620..e30d352c2644 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.stories.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { IndicatorsFiltersContext } from '../../../../context'; +import { IndicatorsFiltersContext } from '../../../../containers/indicators_filters'; import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers'; import { generateMockIndicator } from '../../../../../../../common/types/indicator'; import { IndicatorBlock } from '.'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx index d543b6b6d112..005edd9c4201 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/overview_tab.stories.tsx @@ -10,7 +10,7 @@ import { Story } from '@storybook/react'; import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; import { IndicatorsFlyoutOverview } from '.'; -import { IndicatorsFiltersContext } from '../../../context'; +import { IndicatorsFiltersContext } from '../../../containers/indicators_filters'; export default { component: IndicatorsFlyoutOverview, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx index 014d57b8ec11..60808a46356a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/table_tab/table_tab.stories.tsx @@ -14,7 +14,7 @@ import { mockUiSettingsService } from '../../../../../common/mocks/mock_kibana_u import { mockKibanaTimelinesService } from '../../../../../common/mocks/mock_kibana_timelines_service'; import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator'; import { IndicatorsFlyoutTable } from '.'; -import { IndicatorsFiltersContext } from '../../../context'; +import { IndicatorsFiltersContext } from '../../../containers/indicators_filters'; export default { component: IndicatorsFlyoutTable, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx index 213c750c5d1e..f08e8f3b2f0e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx @@ -120,7 +120,16 @@ export const Default: Story = () => { - + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx index 997cfc7922b9..48d084b0e832 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx @@ -13,6 +13,7 @@ import { TestProvidersComponent } from '../../../../common/mocks/test_providers' import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; import { useFilters } from '../../../query_bar/hooks/use_filters'; +import moment from 'moment'; jest.mock('../../../query_bar/hooks/use_filters'); @@ -47,7 +48,14 @@ describe('', () => { it('should render barchart and field selector dropdown', () => { const component = render( - + ); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx index fab1cfc5473d..31148685e370 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx @@ -9,11 +9,12 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { TimeRange } from '@kbn/es-query'; +import { TimeRangeBounds } from '@kbn/data-plugin/common'; import { SecuritySolutionDataViewBase } from '../../../../types'; import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; -import { useAggregatedIndicators } from '../../hooks/use_aggregated_indicators'; import { IndicatorsFieldSelector } from '../indicators_field_selector/indicators_field_selector'; import { IndicatorsBarChart } from '../indicators_barchart/indicators_barchart'; +import { ChartSeries } from '../../services/fetch_aggregated_indicators'; const DEFAULT_FIELD = RawIndicatorFieldId.Feed; @@ -26,6 +27,14 @@ export interface IndicatorsBarChartWrapperProps { * List of fields coming from the Security Solution sourcerer data view, passed down to the {@link IndicatorFieldSelector} to populate the dropdown. */ indexPattern: SecuritySolutionDataViewBase; + + series: ChartSeries[]; + + dateRange: TimeRangeBounds; + + field: string; + + onFieldChange: (value: string) => void; } /** @@ -33,11 +42,7 @@ export interface IndicatorsBarChartWrapperProps { * and handles retrieving aggregated indicator data. */ export const IndicatorsBarChartWrapper = memo( - ({ timeRange, indexPattern }) => { - const { dateRange, indicators, selectedField, onFieldChange } = useAggregatedIndicators({ - timeRange, - }); - + ({ timeRange, indexPattern, series, dateRange, field, onFieldChange }) => { return ( <> @@ -60,7 +65,7 @@ export const IndicatorsBarChartWrapper = memo( {timeRange ? ( - + ) : ( <> )} diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx index 034fc1363043..6505996a26a7 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx @@ -11,7 +11,7 @@ import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indi import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { IndicatorsTable } from './indicators_table'; -import { IndicatorsFiltersContext } from '../../context'; +import { IndicatorsFiltersContext } from '../../containers/indicators_filters/context'; import { DEFAULT_COLUMNS } from './hooks/use_column_settings'; export default { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/context.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/context.ts similarity index 55% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/context.ts rename to x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/context.ts index b6a4d17754f1..b075668c5015 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/context.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/context.ts @@ -6,13 +6,18 @@ */ import { createContext } from 'react'; -import { FilterManager } from '@kbn/data-plugin/public'; +import { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; +import { Filter, Query, TimeRange } from '@kbn/es-query'; export interface IndicatorsFiltersContextValue { - /** - * FilterManager is used to interact with KQL bar. - */ + timeRange?: TimeRange; + filters: Filter[]; + filterQuery: Query; + handleSavedQuery: (savedQuery: SavedQuery | undefined) => void; + handleSubmitTimeRange: (timeRange?: TimeRange) => void; + handleSubmitQuery: (filterQuery: Query) => void; filterManager: FilterManager; + savedQuery?: SavedQuery; } export const IndicatorsFiltersContext = createContext( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/index.ts new file mode 100644 index 000000000000..4ef23e3e9500 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './indicators_filters'; + +export * from './context'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/indicators_filters.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/indicators_filters.tsx index 0fdd5c60ce5c..2e8a2b55e438 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/indicators_filters.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/containers/indicators_filters/indicators_filters.tsx @@ -5,26 +5,105 @@ * 2.0. */ -import React, { ReactNode, VFC } from 'react'; -import { FilterManager } from '@kbn/data-plugin/public'; -import { IndicatorsFiltersContext, IndicatorsFiltersContextValue } from '../../context'; - -export interface IndicatorsFiltersProps { - /** - * Get {@link FilterManager} from the useFilters hook and save it in context to use within the indicators table. - */ - filterManager: FilterManager; - /** - * Component(s) to be displayed inside - */ - children: ReactNode; -} +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; +import { SavedQuery } from '@kbn/data-plugin/common'; +import { Filter, Query, TimeRange } from '@kbn/es-query'; +import deepEqual from 'fast-deep-equal'; +import { IndicatorsFiltersContext, IndicatorsFiltersContextValue } from './context'; +import { useKibana } from '../../../../hooks/use_kibana'; + +import { + DEFAULT_QUERY, + DEFAULT_TIME_RANGE, + encodeState, + FILTERS_QUERYSTRING_NAMESPACE, + stateFromQueryParams, +} from '../../../query_bar/hooks/use_filters/utils'; /** * Container used to wrap components and share the {@link FilterManager} through React context. */ -export const IndicatorsFilters: VFC = ({ filterManager, children }) => { - const contextValue: IndicatorsFiltersContextValue = { filterManager }; +export const IndicatorsFilters: FC = ({ children }) => { + const { pathname: browserPathName, search } = useLocation(); + const history = useHistory(); + const [savedQuery, setSavedQuery] = useState(undefined); + + const { + services: { + data: { + query: { filterManager }, + }, + }, + } = useKibana(); + + // Filters are picked using the UI widgets + const [filters, setFilters] = useState([]); + + // Time range is self explanatory + const [timeRange, setTimeRange] = useState(DEFAULT_TIME_RANGE); + + // filterQuery is raw kql query that user can type in to filter results + const [filterQuery, setFilterQuery] = useState(DEFAULT_QUERY); + + // Serialize filters into query string + useEffect(() => { + const filterStateAsString = encodeState({ filters, filterQuery, timeRange }); + if (!deepEqual(filterManager.getFilters(), filters)) { + filterManager.setFilters(filters); + } + + history.replace({ + pathname: browserPathName, + search: `${FILTERS_QUERYSTRING_NAMESPACE}=${filterStateAsString}`, + }); + }, [browserPathName, filterManager, filterQuery, filters, history, timeRange]); + + // Sync filterManager to local state (after they are changed from the ui) + useEffect(() => { + const subscription = filterManager.getUpdates$().subscribe(() => { + setFilters(filterManager.getFilters()); + }); + + return () => subscription.unsubscribe(); + }, [filterManager]); + + // Update local state with filter values from the url (on initial mount) + useEffect(() => { + const { + filters: filtersFromQuery, + timeRange: timeRangeFromQuery, + filterQuery: filterQueryFromQuery, + } = stateFromQueryParams(search); + + setTimeRange(timeRangeFromQuery); + setFilterQuery(filterQueryFromQuery); + setFilters(filtersFromQuery); + + // We only want to have it done on initial render with initial 'search' value; + // that is why 'search' is ommited from the deps array + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filterManager]); + + const onSavedQuery = useCallback( + (newSavedQuery: SavedQuery | undefined) => setSavedQuery(newSavedQuery), + [] + ); + + const contextValue: IndicatorsFiltersContextValue = useMemo( + () => ({ + timeRange, + filters, + filterQuery, + handleSavedQuery: onSavedQuery, + handleSubmitTimeRange: setTimeRange, + handleSubmitQuery: setFilterQuery, + filterManager, + savedQuery, + }), + [filterManager, filterQuery, filters, onSavedQuery, savedQuery, timeRange] + ); return ( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx index 6b3feb740690..85c703cf5dca 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx @@ -12,33 +12,22 @@ import { mockedTimefilterService, TestProvidersComponent, } from '../../../common/mocks/test_providers'; -import { useFilters } from '../../query_bar/hooks/use_filters'; import { createFetchAggregatedIndicators } from '../services/fetch_aggregated_indicators'; jest.mock('../services/fetch_aggregated_indicators'); -jest.mock('../../query_bar/hooks/use_filters'); const useAggregatedIndicatorsParams: UseAggregatedIndicatorsParam = { timeRange: DEFAULT_TIME_RANGE, + filters: [], + filterQuery: { language: 'kuery', query: '' }, }; -const stub = () => {}; - const renderUseAggregatedIndicators = () => - renderHook((props) => useAggregatedIndicators(props), { + renderHook((props: UseAggregatedIndicatorsParam) => useAggregatedIndicators(props), { initialProps: useAggregatedIndicatorsParams, wrapper: TestProvidersComponent, }); -const initialFiltersValue = { - filters: [], - filterQuery: { language: 'kuery', query: '' }, - filterManager: {} as any, - handleSavedQuery: stub, - handleSubmitQuery: stub, - handleSubmitTimeRange: stub, -}; - describe('useAggregatedIndicators()', () => { beforeEach(jest.clearAllMocks); @@ -56,14 +45,12 @@ describe('useAggregatedIndicators()', () => { (createFetchAggregatedIndicators as MockedCreateFetchAggregatedIndicators).mockReturnValue( aggregatedIndicatorsQuery ); - - (useFilters as jest.MockedFunction).mockReturnValue(initialFiltersValue); }); it('should create and call the aggregatedIndicatorsQuery correctly', async () => { aggregatedIndicatorsQuery.mockResolvedValue([]); - const { rerender } = renderUseAggregatedIndicators(); + const { result, rerender } = renderUseAggregatedIndicators(); // indicators service and the query should be called just once expect( @@ -81,14 +68,12 @@ describe('useAggregatedIndicators()', () => { expect.any(AbortSignal) ); - // After filter values change, the hook will be re-rendered and should call the query function again, with - // updated values - (useFilters as jest.MockedFunction).mockReturnValue({ - ...initialFiltersValue, - filterQuery: { language: 'kuery', query: "threat.indicator.type: 'file'" }, - }); - - await act(async () => rerender()); + await act(async () => + rerender({ + filterQuery: { language: 'kuery', query: "threat.indicator.type: 'file'" }, + filters: [], + }) + ); expect(aggregatedIndicatorsQuery).toHaveBeenCalledTimes(2); expect(aggregatedIndicatorsQuery).toHaveBeenLastCalledWith( @@ -97,5 +82,16 @@ describe('useAggregatedIndicators()', () => { }), expect.any(AbortSignal) ); + expect(result.current).toMatchInlineSnapshot(` + Object { + "dateRange": Object { + "max": "2022-01-02T00:00:00.000Z", + "min": "2022-01-01T00:00:00.000Z", + }, + "onFieldChange": [Function], + "selectedField": "threat.feed.name", + "series": Array [], + } + `); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts index 2609dbda5eb1..98e672ac3ad9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { TimeRange } from '@kbn/es-query'; +import { useQuery } from '@tanstack/react-query'; +import { Filter, Query, TimeRange } from '@kbn/es-query'; import { useMemo, useState } from 'react'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; -import { useQuery } from '@tanstack/react-query'; import { useInspector } from '../../../hooks/use_inspector'; -import { useFilters } from '../../query_bar/hooks/use_filters'; import { RawIndicatorFieldId } from '../../../../common/types/indicator'; import { useKibana } from '../../../hooks/use_kibana'; import { DEFAULT_TIME_RANGE } from '../../query_bar/hooks/use_filters/utils'; @@ -27,13 +26,15 @@ export interface UseAggregatedIndicatorsParam { * to query indicators for the Indicators barchart. */ timeRange?: TimeRange; + filters: Filter[]; + filterQuery: Query; } export interface UseAggregatedIndicatorsValue { /** * Array of {@link ChartSeries}, ready to be used in the Indicators barchart. */ - indicators: ChartSeries[]; + series: ChartSeries[]; /** * Callback used by the IndicatorsFieldSelector component to query a new set of * aggregated indicators. @@ -54,6 +55,8 @@ const DEFAULT_FIELD = RawIndicatorFieldId.Feed; export const useAggregatedIndicators = ({ timeRange = DEFAULT_TIME_RANGE, + filters, + filterQuery, }: UseAggregatedIndicatorsParam): UseAggregatedIndicatorsValue => { const { services: { @@ -66,7 +69,6 @@ export const useAggregatedIndicators = ({ const { inspectorAdapters } = useInspector(); const [field, setField] = useState(DEFAULT_FIELD); - const { filters, filterQuery } = useFilters(); const aggregatedIndicatorsQuery = useMemo( () => @@ -105,7 +107,7 @@ export const useAggregatedIndicators = ({ return { dateRange, - indicators: data || [], + series: data || [], onFieldChange: setField, selectedField: field, }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx index 0b0620813078..7292dbcd1b03 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.test.tsx @@ -98,6 +98,27 @@ describe('useIndicators()', () => { }), expect.any(AbortSignal) ); + + expect(hookResult.result.current).toMatchInlineSnapshot(` + Object { + "handleRefresh": [Function], + "indicatorCount": 0, + "indicators": Array [], + "isFetching": true, + "isLoading": true, + "onChangeItemsPerPage": [Function], + "onChangePage": [Function], + "pagination": Object { + "pageIndex": 0, + "pageSize": 50, + "pageSizeOptions": Array [ + 10, + 25, + 50, + ], + }, + } + `); }); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts index 5303b5dae06c..1855d411eb8c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts @@ -8,6 +8,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { Filter, Query, TimeRange } from '@kbn/es-query'; import { useQuery } from '@tanstack/react-query'; +import { EuiDataGridSorting } from '@elastic/eui'; import { useInspector } from '../../../hooks/use_inspector'; import { Indicator } from '../../../../common/types/indicator'; import { useKibana } from '../../../hooks/use_kibana'; @@ -22,11 +23,15 @@ export interface UseIndicatorsParams { filterQuery: Query; filters: Filter[]; timeRange?: TimeRange; - sorting: any[]; + sorting: EuiDataGridSorting['columns']; } export interface UseIndicatorsValue { handleRefresh: () => void; + + /** + * Array of {@link Indicator} ready to render inside the IndicatorTable component + */ indicators: Indicator[]; indicatorCount: number; pagination: Pagination; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_filters_context.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_filters_context.ts index e8cc4b18474b..e4c7c48d03d1 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_filters_context.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators_filters_context.ts @@ -6,7 +6,10 @@ */ import { useContext } from 'react'; -import { IndicatorsFiltersContext, IndicatorsFiltersContextValue } from '../context'; +import { + IndicatorsFiltersContext, + IndicatorsFiltersContextValue, +} from '../containers/indicators_filters/context'; /** * Hook to retrieve {@link IndicatorsFiltersContext} (contains FilterManager) diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx index 11bdb8fc8e6e..7740345f468d 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.test.tsx @@ -27,7 +27,7 @@ describe('', () => { useAggregatedIndicators as jest.MockedFunction ).mockReturnValue({ dateRange: { min: moment(), max: moment() }, - indicators: [], + series: [], selectedField: '', onFieldChange: () => {}, }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx index f51e062e1c3c..fff2caad5715 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx @@ -7,7 +7,6 @@ import React, { FC, VFC } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { IndicatorsFilters } from './containers/indicators_filters/indicators_filters'; import { IndicatorsBarChartWrapper } from './components/indicators_barchart_wrapper/indicators_barchart_wrapper'; import { IndicatorsTable } from './components/indicators_table/indicators_table'; import { useIndicators } from './hooks/use_indicators'; @@ -19,14 +18,18 @@ import { useSourcererDataView } from './hooks/use_sourcerer_data_view'; import { FieldTypesProvider } from '../../containers/field_types_provider'; import { InspectorProvider } from '../../containers/inspector'; import { useColumnSettings } from './components/indicators_table/hooks/use_column_settings'; +import { useAggregatedIndicators } from './hooks/use_aggregated_indicators'; +import { IndicatorsFilters } from './containers/indicators_filters'; const queryClient = new QueryClient(); const IndicatorsPageProviders: FC = ({ children }) => ( - - {children} - + + + {children} + + ); @@ -46,43 +49,68 @@ const IndicatorsPageContent: VFC = () => { savedQuery, } = useFilters(); - const { handleRefresh, ...indicators } = useIndicators({ + const { + handleRefresh, + indicatorCount, + indicators, + isLoading, + onChangeItemsPerPage, + onChangePage, + pagination, + } = useIndicators({ filters, filterQuery, timeRange, sorting: columnSettings.sorting.columns, }); + const { dateRange, series, selectedField, onFieldChange } = useAggregatedIndicators({ + timeRange, + filters, + filterQuery, + }); + return ( - - - + + + + + - - - - - + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx index 1e6c7d0c2614..08297774e51f 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Story } from '@storybook/react'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; -import { IndicatorsFiltersContext } from '../../../indicators/context'; +import { IndicatorsFiltersContext } from '../../../indicators/containers/indicators_filters/context'; import { FilterIn } from '.'; export default { diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx index a2f0061d36a9..cf625c23754a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Story } from '@storybook/react'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; -import { IndicatorsFiltersContext } from '../../../indicators/context'; +import { IndicatorsFiltersContext } from '../../../indicators/containers/indicators_filters/context'; import { FilterOut } from '.'; export default { diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.test.tsx index b9c16240df4b..4d34c9ee6ff0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.test.tsx @@ -9,28 +9,37 @@ import { mockUseKibanaForFilters } from '../../../../common/mocks/mock_use_kiban import { renderHook, act, RenderHookResult, Renderer } from '@testing-library/react-hooks'; import { useFilters, UseFiltersValue } from './use_filters'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useLocation, useHistory } from 'react-router-dom'; import { Filter } from '@kbn/es-query'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; +import React, { FC } from 'react'; +import { IndicatorsFilters } from '../../../indicators/containers/indicators_filters'; jest.mock('react-router-dom', () => ({ - useLocation: jest.fn().mockReturnValue({ - search: '', - }), + MemoryRouter: ({ children }: any) => <>{children}, + useLocation: jest.fn().mockReturnValue({ pathname: '', search: '' }), useHistory: jest.fn().mockReturnValue({ replace: jest.fn() }), })); +const FiltersWrapper: FC = ({ children }) => ( + + {children}{' '} + +); + +const renderUseFilters = () => renderHook(() => useFilters(), { wrapper: FiltersWrapper }); + describe('useFilters()', () => { let hookResult: RenderHookResult<{}, UseFiltersValue, Renderer>; let mockRef: ReturnType; + afterAll(() => jest.unmock('react-router-dom')); + describe('when mounted', () => { beforeEach(async () => { mockRef = mockUseKibanaForFilters(); - hookResult = renderHook(() => useFilters(), { - wrapper: TestProvidersComponent, - }); + hookResult = renderUseFilters(); }); it('should have valid initial filterQuery value', () => { @@ -44,9 +53,7 @@ describe('useFilters()', () => { '?indicators=(filterQuery:(language:kuery,query:%27threat.indicator.type%20:%20"file"%20%27),filters:!(),timeRange:(from:now/d,to:now/d))', }); - hookResult = renderHook(() => useFilters(), { - wrapper: TestProvidersComponent, - }); + hookResult = renderUseFilters(); expect(hookResult.result.current.filterQuery).toMatchObject({ language: 'kuery', @@ -61,9 +68,7 @@ describe('useFilters()', () => { beforeEach(async () => { mockRef = mockUseKibanaForFilters(); - hookResult = renderHook(() => useFilters(), { - wrapper: TestProvidersComponent, - }); + hookResult = renderUseFilters(); (useHistory as jest.Mock).mockReturnValue({ replace: historyReplace }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.ts index 73ce49691d5a..c50a10c17b70 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filters/use_filters.ts @@ -5,110 +5,24 @@ * 2.0. */ -import { Filter, Query, TimeRange } from '@kbn/es-query'; -import { useCallback, useEffect, useState } from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import deepEqual from 'fast-deep-equal'; -import type { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; -import { useKibana } from '../../../../hooks/use_kibana'; +import { useContext } from 'react'; import { - DEFAULT_QUERY, - DEFAULT_TIME_RANGE, - encodeState, - FILTERS_QUERYSTRING_NAMESPACE, - stateFromQueryParams, -} from './utils'; + IndicatorsFiltersContext, + IndicatorsFiltersContextValue, +} from '../../../indicators/containers/indicators_filters'; -export interface UseFiltersValue { - timeRange?: TimeRange; - filters: Filter[]; - filterQuery: Query; - handleSavedQuery: (savedQuery: SavedQuery | undefined) => void; - handleSubmitTimeRange: (timeRange?: TimeRange) => void; - handleSubmitQuery: (filterQuery: Query) => void; - filterManager: FilterManager; - savedQuery?: SavedQuery; -} +export type UseFiltersValue = IndicatorsFiltersContextValue; /** * Custom react hook housing logic for KQL bar * @returns Filters and TimeRange for use with KQL bar */ -export const useFilters = (): UseFiltersValue => { - const { pathname: browserPathName, search } = useLocation(); - const history = useHistory(); - const [savedQuery, setSavedQuery] = useState(undefined); +export const useFilters = () => { + const contextValue = useContext(IndicatorsFiltersContext); - const { - services: { - data: { - query: { filterManager }, - }, - }, - } = useKibana(); + if (!contextValue) { + throw new Error('Filters can only be used inside IndicatorFiltersContext'); + } - // Filters are picked using the UI widgets - const [filters, setFilters] = useState([]); - - // Time range is self explanatory - const [timeRange, setTimeRange] = useState(DEFAULT_TIME_RANGE); - - // filterQuery is raw kql query that user can type in to filter results - const [filterQuery, setFilterQuery] = useState(DEFAULT_QUERY); - - // Serialize filters into query string - useEffect(() => { - const filterStateAsString = encodeState({ filters, filterQuery, timeRange }); - if (!deepEqual(filterManager.getFilters(), filters)) { - filterManager.setFilters(filters); - } - - history.replace({ - pathname: browserPathName, - search: `${FILTERS_QUERYSTRING_NAMESPACE}=${filterStateAsString}`, - }); - }, [browserPathName, filterManager, filterQuery, filters, history, timeRange]); - - // Sync filterManager to local state (after they are changed from the ui) - useEffect(() => { - const subscription = filterManager.getUpdates$().subscribe(() => { - setFilters(filterManager.getFilters()); - }); - - return () => subscription.unsubscribe(); - }, [filterManager]); - - // Update local state with filter values from the url (on initial mount) - useEffect(() => { - const { - filters: filtersFromQuery, - timeRange: timeRangeFromQuery, - filterQuery: filterQueryFromQuery, - } = stateFromQueryParams(search); - - setTimeRange(timeRangeFromQuery); - setFilterQuery(filterQueryFromQuery); - setFilters(filtersFromQuery); - - // We only want to have it done on initial render with initial 'search' value; - // that is why 'search' is ommited from the deps array - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filterManager]); - - const onSavedQuery = useCallback( - (newSavedQuery: SavedQuery | undefined) => setSavedQuery(newSavedQuery), - [] - ); - - return { - timeRange, - filters, - filterQuery, - handleSavedQuery: onSavedQuery, - handleSubmitTimeRange: setTimeRange, - handleSubmitQuery: setFilterQuery, - filterManager, - savedQuery, - }; + return contextValue; }; From 45e80b2a477f99c63dc3245efa471bd81c12268a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 28 Sep 2022 22:44:53 -0600 Subject: [PATCH 094/185] [api-docs] Daily api_docs build (#142178) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.devdocs.json | 139 +- api_docs/alerting.mdx | 4 +- api_docs/apm.devdocs.json | 24 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.devdocs.json | 32 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.devdocs.json | 253 +- api_docs/core.mdx | 4 +- api_docs/custom_integrations.devdocs.json | 23 +- api_docs/custom_integrations.mdx | 4 +- api_docs/dashboard.devdocs.json | 3835 ++++++----------- api_docs/dashboard.mdx | 10 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.devdocs.json | 8 - api_docs/data_query.mdx | 2 +- api_docs/data_search.devdocs.json | 12 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 14 +- api_docs/deprecations_by_plugin.mdx | 14 +- api_docs/deprecations_by_team.mdx | 7 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.devdocs.json | 4 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.devdocs.json | 6 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.devdocs.json | 8 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/fleet.devdocs.json | 66 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.devdocs.json | 6 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...equest_handler_context_server.devdocs.json | 283 ++ ...re_http_request_handler_context_server.mdx | 33 + .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- .../kbn_core_injected_metadata_browser.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- ...ore_saved_objects_api_browser.devdocs.json | 6 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- ...core_saved_objects_api_server.devdocs.json | 6 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.devdocs.json | 6 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ion_exception_list_components.devdocs.json | 1209 ++++++ ...ritysolution_exception_list_components.mdx | 39 + api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.devdocs.json | 8 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.devdocs.json | 26 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 20 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.devdocs.json | 4 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.devdocs.json | 35 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.devdocs.json | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.devdocs.json | 8 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.devdocs.json | 8 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.devdocs.json | 4 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.devdocs.json | 16 +- api_docs/triggers_actions_ui.mdx | 4 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.devdocs.json | 207 +- api_docs/visualizations.mdx | 4 +- 432 files changed, 3973 insertions(+), 3212 deletions(-) create mode 100644 api_docs/kbn_core_http_request_handler_context_server.devdocs.json create mode 100644 api_docs/kbn_core_http_request_handler_context_server.mdx create mode 100644 api_docs/kbn_securitysolution_exception_list_components.devdocs.json create mode 100644 api_docs/kbn_securitysolution_exception_list_components.mdx diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 4d0790ec9518..929e069b2bf5 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: 2022-09-28 +date: 2022-09-29 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 88e4157414a7..43856104f328 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index c3bd7eb349a0..7694fbde6190 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index a19bdb320740..9971fcee0a58 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -2666,8 +2666,6 @@ "Alert", "; scheduleActions: (actionGroup: ActionGroupIds, context?: Context) => ", "Alert", - "; scheduleActionsWithSubGroup: (actionGroup: ActionGroupIds, subgroup: string, context?: Context) => ", - "Alert", "; setContext: (context: Context) => ", "Alert", "; getContext: () => Context; hasContext: () => boolean; }" @@ -2832,7 +2830,11 @@ "section": "def-common.IExecutionErrorsResult", "text": "IExecutionErrorsResult" }, - ">; bulkEdit: ; getGlobalExecutionKpiWithAuth: ({ dateStart, dateEnd, filter, }: ", + "GetGlobalExecutionKPIParams", + ") => Promise<{ success: number; unknown: number; failure: number; activeAlerts: number; newAlerts: number; recoveredAlerts: number; erroredActions: number; triggeredActions: number; }>; getRuleExecutionKPI: ({ id, dateStart, dateEnd, filter }: ", + "GetRuleExecutionKPIParams", + ") => Promise<{ success: number; unknown: number; failure: number; activeAlerts: number; newAlerts: number; recoveredAlerts: number; erroredActions: number; triggeredActions: number; }>; bulkEdit: ; }; observability: { setup: { getScopedAnnotationsClient: (requestContext: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { licensing: Promise<", { "pluginId": "licensing", @@ -5696,13 +5684,7 @@ "label": "context", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { licensing: Promise<", { "pluginId": "licensing", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index d9218c6f9c48..934ffbbd3380 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index b574eadcc22f..12a25911e2da 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.devdocs.json b/api_docs/bfetch.devdocs.json index 74f1cf1aee45..f060159bc0f3 100644 --- a/api_docs/bfetch.devdocs.json +++ b/api_docs/bfetch.devdocs.json @@ -357,13 +357,7 @@ "(path: string, params: (request: ", "KibanaRequest", ", context: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ") => ", { "pluginId": "bfetch", @@ -375,13 +369,7 @@ ", method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | undefined, pluginRouter?: ", "IRouter", "<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", "> | undefined) => void" ], "path": "src/plugins/bfetch/server/plugin.ts", @@ -414,13 +402,7 @@ "(request: ", "KibanaRequest", ", context: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ") => ", { "pluginId": "bfetch", @@ -461,13 +443,7 @@ "signature": [ "IRouter", "<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", "> | undefined" ], "path": "src/plugins/bfetch/server/plugin.ts", diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index e8fa3ead871d..c4055d656405 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: 2022-09-28 +date: 2022-09-29 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 9c484f7dfc61..4ef1b1b03a0e 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: 2022-09-28 +date: 2022-09-29 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 2ec486a4957e..6df29978541a 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: 2022-09-28 +date: 2022-09-29 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 b204ddb7b0ee..718e93231dff 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: 2022-09-28 +date: 2022-09-29 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 fdf2f80ec284..74692e016ccf 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 33e642108103..9b5efb2aa51f 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 8cdffb406e1d..1ccfb42f61d0 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: 2022-09-28 +date: 2022-09-29 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 e365d6a87afe..00e0779286aa 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 69b2dfc22a26..e1427ab3d19f 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index 607bae8fad1f..67fa46292601 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -10389,7 +10389,7 @@ "\nThe outcome for a successful `resolve` call is one of the following values:\n\n * `'exactMatch'` -- One document exactly matched the given ID.\n * `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different\n than the given ID.\n * `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the\n `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID." ], "signature": [ - "\"exactMatch\" | \"aliasMatch\" | \"conflict\"" + "\"conflict\" | \"exactMatch\" | \"aliasMatch\"" ], "path": "node_modules/@types/kbn__core-saved-objects-api-browser/index.d.ts", "deprecated": false, @@ -10850,6 +10850,14 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx" }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts" + }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx" @@ -11184,11 +11192,15 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts" + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts" + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts" }, { "plugin": "dashboard", @@ -11232,43 +11244,35 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "ml", @@ -11278,6 +11282,14 @@ "plugin": "ml", "path": "x-pack/plugins/ml/common/types/modules.ts" }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts" + }, { "plugin": "@kbn/core-saved-objects-server-internal", "path": "packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts" @@ -15356,9 +15368,9 @@ "label": "SavedObjectsFindOptions", "description": [], "signature": [ - "{ type: string | string[]; filter?: any; search?: string | undefined; aggs?: Record | undefined; fields?: string[] | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; searchFields?: string[] | undefined; hasReference?: ", + "> | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; searchFields?: string[] | undefined; hasReference?: ", "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", @@ -21534,13 +21546,7 @@ "signature": [ "HttpServicePreboot", "<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ">" ], "path": "src/core/server/index.ts", @@ -21575,7 +21581,10 @@ "description": [ "\nThe `core` context provided to route handler.\n\nProvides the following clients and services:\n - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client\n which uses the credentials of the incoming request\n - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing\n all the registered types.\n - {@link IScopedClusterClient | elasticsearch.client} - Elasticsearch\n data client which uses the credentials of the incoming request\n - {@link IUiSettingsClient | uiSettings.client} - uiSettings client\n which uses the credentials of the incoming request" ], - "path": "src/core/server/core_route_handler_context.ts", + "signature": [ + "CoreRequestHandlerContext" + ], + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -21589,7 +21598,7 @@ "signature": [ "SavedObjectsRequestHandlerContext" ], - "path": "src/core/server/core_route_handler_context.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -21603,7 +21612,7 @@ "signature": [ "ElasticsearchRequestHandlerContext" ], - "path": "src/core/server/core_route_handler_context.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -21617,7 +21626,7 @@ "signature": [ "UiSettingsRequestHandlerContext" ], - "path": "src/core/server/core_route_handler_context.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false }, @@ -21631,7 +21640,7 @@ "signature": [ "DeprecationsRequestHandlerContext" ], - "path": "src/core/server/core_route_handler_context.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false } @@ -21802,13 +21811,7 @@ "signature": [ "HttpServiceSetup", "<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", "> & { resources: ", { "pluginId": "core", @@ -25950,21 +25953,9 @@ ], "signature": [ "(route: ", "RouteConfig", ", handler: ", @@ -39420,6 +39411,37 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.PrebootCoreRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "PrebootCoreRequestHandlerContext", + "description": [], + "signature": [ + "PrebootCoreRequestHandlerContext" + ], + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.PrebootCoreRequestHandlerContext.uiSettings", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + "PrebootUiSettingsRequestHandlerContext" + ], + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.PrebootPlugin", @@ -39523,6 +39545,41 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.PrebootRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "PrebootRequestHandlerContext", + "description": [], + "signature": [ + "PrebootRequestHandlerContext", + " extends ", + "RequestHandlerContextBase" + ], + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.PrebootRequestHandlerContext.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + "Promise<", + "PrebootCoreRequestHandlerContext", + ">" + ], + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.RegisterDeprecationsConfig", @@ -39588,17 +39645,11 @@ "\nBase context passed to a route handler, containing the `core` context part.\n" ], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " extends ", "RequestHandlerContextBase" ], - "path": "src/core/server/index.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -39611,16 +39662,10 @@ "description": [], "signature": [ "Promise<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreRequestHandlerContext", - "text": "CoreRequestHandlerContext" - }, + "CoreRequestHandlerContext", ">" ], - "path": "src/core/server/index.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false } @@ -40487,6 +40532,14 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx" }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts" + }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx" @@ -40821,11 +40874,15 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts" + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts" + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts" }, { "plugin": "dashboard", @@ -40869,43 +40926,35 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/common/saved_dashboard_references.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts" }, { "plugin": "ml", @@ -40915,6 +40964,14 @@ "plugin": "ml", "path": "x-pack/plugins/ml/common/types/modules.ts" }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts" + }, { "plugin": "@kbn/core-saved-objects-server-internal", "path": "packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts" @@ -45734,11 +45791,11 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/migrations_730.ts" + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/server/saved_objects/migrations_730.ts" + "path": "src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts" }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", @@ -46719,7 +46776,7 @@ "\nThe outcome for a successful `resolve` call is one of the following values:\n\n * `'exactMatch'` -- One document exactly matched the given ID.\n * `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different\n than the given ID.\n * `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the\n `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID." ], "signature": [ - "\"exactMatch\" | \"aliasMatch\" | \"conflict\"" + "\"conflict\" | \"exactMatch\" | \"aliasMatch\"" ], "path": "node_modules/@types/kbn__core-saved-objects-api-server/index.d.ts", "deprecated": false, @@ -50176,16 +50233,10 @@ "\nMixin allowing plugins to define their own request handler contexts.\n" ], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; }" ], - "path": "src/core/server/index.ts", + "path": "node_modules/@types/kbn__core-http-request-handler-context-server/index.d.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -53662,9 +53713,9 @@ "label": "SavedObjectsCreatePointInTimeFinderOptions", "description": [], "signature": [ - "{ type: string | string[]; filter?: any; search?: string | undefined; aggs?: Record | undefined; fields?: string[] | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", + "> | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", "SortOrder", " | undefined; searchFields?: string[] | undefined; rootSearchFields?: string[] | undefined; hasReference?: ", "SavedObjectsFindOptionsReference", diff --git a/api_docs/core.mdx b/api_docs/core.mdx index d554613d1ec1..798c6c53ce34 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2684 | 0 | 35 | 0 | +| 2688 | 0 | 30 | 0 | ## Client diff --git a/api_docs/custom_integrations.devdocs.json b/api_docs/custom_integrations.devdocs.json index 7a339106c7a6..03090384a055 100644 --- a/api_docs/custom_integrations.devdocs.json +++ b/api_docs/custom_integrations.devdocs.json @@ -438,7 +438,7 @@ "label": "categories", "description": [], "signature": [ - "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" + "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -488,7 +488,7 @@ "\nA category applicable to an Integration." ], "signature": [ - "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" + "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -728,7 +728,7 @@ "label": "categories", "description": [], "signature": [ - "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" + "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -838,7 +838,7 @@ "label": "id", "description": [], "signature": [ - "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" + "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -860,7 +860,7 @@ "\nThe list of all available categories." ], "signature": [ - "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" + "(\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\")[]" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -877,7 +877,7 @@ "\nA category applicable to an Integration." ], "signature": [ - "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" + "\"custom\" | \"enterprise_search\" | \"sample_data\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"microsoft_365\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\" | \"communications\" | \"file_storage\" | \"language_client\" | \"upload_file\" | \"website_search\" | \"geo\"" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false, @@ -1097,6 +1097,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "customIntegrations", + "id": "def-common.INTEGRATION_CATEGORY_DISPLAY.infrastructure", + "type": "string", + "tags": [], + "label": "infrastructure", + "description": [], + "path": "src/plugins/custom_integrations/common/index.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "customIntegrations", "id": "def-common.INTEGRATION_CATEGORY_DISPLAY.kubernetes", diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index b25052db9516..77c928f34e58 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 103 | 0 | 84 | 1 | +| 104 | 0 | 85 | 1 | ## Client diff --git a/api_docs/dashboard.devdocs.json b/api_docs/dashboard.devdocs.json index 447c0d0af07e..c185507fd910 100644 --- a/api_docs/dashboard.devdocs.json +++ b/api_docs/dashboard.devdocs.json @@ -1,33 +1,99 @@ { "id": "dashboard", "client": { - "classes": [ + "classes": [], + "functions": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer", - "type": "Class", + "id": "def-public.cleanEmptyKeys", + "type": "Function", "tags": [], - "label": "DashboardContainer", + "label": "cleanEmptyKeys", "description": [], "signature": [ + "(stateObj: Record) => Record" + ], + "path": "src/plugins/dashboard/public/locator.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainer", - "text": "DashboardContainer" - }, - " extends ", + "parentPluginId": "dashboard", + "id": "def-public.cleanEmptyKeys.$1", + "type": "Object", + "tags": [], + "label": "stateObj", + "description": [], + "signature": [ + "Record" + ], + "path": "src/plugins/dashboard/public/locator.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.createDashboardEditUrl", + "type": "Function", + "tags": [], + "label": "createDashboardEditUrl", + "description": [], + "signature": [ + "(id: string | undefined, editMode: boolean | undefined) => string" + ], + "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.Container", - "text": "Container" + "parentPluginId": "dashboard", + "id": "def-public.createDashboardEditUrl.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false }, - "<", - "InheritedChildInput", - ", ", + { + "parentPluginId": "dashboard", + "id": "def-public.createDashboardEditUrl.$2", + "type": "CompoundType", + "tags": [], + "label": "editMode", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardContainerInput", + "type": "Interface", + "tags": [], + "label": "DashboardContainerInput", + "description": [], + "signature": [ { "pluginId": "dashboard", "scope": "public", @@ -35,1674 +101,1479 @@ "section": "def-public.DashboardContainerInput", "text": "DashboardContainerInput" }, - ", ", + " extends ", { "pluginId": "embeddable", "scope": "public", "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" + "section": "def-public.ContainerInput", + "text": "ContainerInput" }, - ">" + "<{}>" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"dashboard\"" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.controlGroup", + "id": "def-public.DashboardContainerInput.controlGroupInput", "type": "Object", "tags": [], - "label": "controlGroup", + "label": "controlGroupInput", "description": [], "signature": [ { "pluginId": "controls", - "scope": "public", + "scope": "common", "docId": "kibControlsPluginApi", - "section": "def-public.ControlGroupContainer", - "text": "ControlGroupContainer" + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" }, " | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.getAllDataViews", - "type": "Function", + "id": "def-public.DashboardContainerInput.refreshConfig", + "type": "Object", "tags": [], - "label": "getAllDataViews", - "description": [ - "\nGets all the dataviews that are actively being used in the dashboard" - ], + "label": "refreshConfig", + "description": [], "signature": [ - "() => ", { - "pluginId": "dataViews", + "pluginId": "data", "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" }, - "[]" + " | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [ - "An array of dataviews" - ] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.setAllDataViews", - "type": "Function", + "id": "def-public.DashboardContainerInput.isEmbeddedExternally", + "type": "CompoundType", "tags": [], - "label": "setAllDataViews", - "description": [ - "\nUse this to set the dataviews that are used in the dashboard when they change/update" - ], + "label": "isEmbeddedExternally", + "description": [], "signature": [ - "(newDataViews: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - "[]) => void" + "boolean | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.setAllDataViews.$1", - "type": "Array", - "tags": [], - "label": "newDataViews", - "description": [ - "The new array of dataviews that will overwrite the old dataviews array" - ], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - "[]" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.getPanelCount", - "type": "Function", + "id": "def-public.DashboardContainerInput.isFullScreenMode", + "type": "boolean", "tags": [], - "label": "getPanelCount", + "label": "isFullScreenMode", "description": [], - "signature": [ - "() => number" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.getPanelTitles", - "type": "Function", + "id": "def-public.DashboardContainerInput.expandedPanelId", + "type": "string", "tags": [], - "label": "getPanelTitles", + "label": "expandedPanelId", "description": [], "signature": [ - "() => Promise" + "string | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.Unnamed", - "type": "Function", + "id": "def-public.DashboardContainerInput.timeRange", + "type": "Object", "tags": [], - "label": "Constructor", + "label": "timeRange", "description": [], "signature": [ - "any" + "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "initialInput", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" - } - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.Unnamed.$2", - "type": "Object", - "tags": [], - "label": "parent", - "description": [], - "signature": [ - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.Container", - "text": "Container" - }, - "<{}, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerInput", - "text": "ContainerInput" - }, - "<{}>, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" - }, - "> | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.Unnamed.$3", - "type": "CompoundType", - "tags": [], - "label": "controlGroup", - "description": [], - "signature": [ - { - "pluginId": "controls", - "scope": "public", - "docId": "kibControlsPluginApi", - "section": "def-public.ControlGroupContainer", - "text": "ControlGroupContainer" - }, - " | ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ErrorEmbeddable", - "text": "ErrorEmbeddable" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.createNewPanelState", - "type": "Function", + "id": "def-public.DashboardContainerInput.timeslice", + "type": "Object", "tags": [], - "label": "createNewPanelState", + "label": "timeslice", "description": [], "signature": [ - ">(factory: ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.EmbeddableFactory", - "text": "EmbeddableFactory" - }, - ", partial?: Partial) => ", - "DashboardPanelState", - "" + "[number, number] | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.createNewPanelState.$1", - "type": "Object", - "tags": [], - "label": "factory", - "description": [], - "signature": [ - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.EmbeddableFactory", - "text": "EmbeddableFactory" - }, - "" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.createNewPanelState.$2", - "type": "Object", - "tags": [], - "label": "partial", - "description": [], - "signature": [ - "Partial" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.showPlaceholderUntil", - "type": "Function", + "id": "def-public.DashboardContainerInput.timeRestore", + "type": "boolean", "tags": [], - "label": "showPlaceholderUntil", + "label": "timeRestore", "description": [], - "signature": [ - "(newStateComplete: Promise>>, placementMethod?: ", - "PanelPlacementMethod", - " | undefined, placementArgs?: TPlacementMethodArgs | undefined) => void" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.showPlaceholderUntil.$1", - "type": "Object", - "tags": [], - "label": "newStateComplete", - "description": [], - "signature": [ - "Promise>>" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.showPlaceholderUntil.$2", - "type": "Function", - "tags": [], - "label": "placementMethod", - "description": [], - "signature": [ - "PanelPlacementMethod", - " | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.showPlaceholderUntil.$3", - "type": "Uncategorized", - "tags": [], - "label": "placementArgs", - "description": [], - "signature": [ - "TPlacementMethodArgs | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.replacePanel", - "type": "Function", + "id": "def-public.DashboardContainerInput.description", + "type": "string", "tags": [], - "label": "replacePanel", + "label": "description", "description": [], "signature": [ - "(previousPanelState: ", - "DashboardPanelState", - "<", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableInput", - "text": "EmbeddableInput" - }, - ">, newPanelState: Partial<", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.PanelState", - "text": "PanelState" - }, - "<{ id: string; }>>, generateNewId?: boolean | undefined) => void" + "string | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.replacePanel.$1", - "type": "Object", - "tags": [], - "label": "previousPanelState", - "description": [], - "signature": [ - "DashboardPanelState", - "<", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableInput", - "text": "EmbeddableInput" - }, - ">" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.replacePanel.$2", - "type": "Object", - "tags": [], - "label": "newPanelState", - "description": [], - "signature": [ - "Partial<", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.PanelState", - "text": "PanelState" - }, - "<{ id: string; }>>" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.replacePanel.$3", - "type": "CompoundType", - "tags": [], - "label": "generateNewId", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.addOrUpdateEmbeddable", - "type": "Function", + "id": "def-public.DashboardContainerInput.useMargins", + "type": "boolean", "tags": [], - "label": "addOrUpdateEmbeddable", + "label": "useMargins", "description": [], - "signature": [ - " = ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.IEmbeddable", - "text": "IEmbeddable" - }, - ">(type: string, explicitInput: Partial, embeddableId?: string | undefined) => Promise" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.addOrUpdateEmbeddable.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.addOrUpdateEmbeddable.$2", - "type": "Object", - "tags": [], - "label": "explicitInput", - "description": [], - "signature": [ - "Partial" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.addOrUpdateEmbeddable.$3", - "type": "string", - "tags": [], - "label": "embeddableId", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.render", - "type": "Function", + "id": "def-public.DashboardContainerInput.syncColors", + "type": "CompoundType", "tags": [], - "label": "render", + "label": "syncColors", "description": [], "signature": [ - "(dom: HTMLElement) => void" + "boolean | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.render.$1", - "type": "Object", - "tags": [], - "label": "dom", - "description": [], - "signature": [ - "HTMLElement" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.destroy", - "type": "Function", + "id": "def-public.DashboardContainerInput.syncTooltips", + "type": "CompoundType", "tags": [], - "label": "destroy", + "label": "syncTooltips", "description": [], "signature": [ - "() => void" + "boolean | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.getInheritedInput", - "type": "Function", + "id": "def-public.DashboardContainerInput.viewMode", + "type": "Enum", "tags": [], - "label": "getInheritedInput", + "label": "viewMode", "description": [], "signature": [ - "(id: string) => ", - "InheritedChildInput" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainer.getInheritedInput.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.ViewMode", + "text": "ViewMode" } ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition", - "type": "Class", - "tags": [], - "label": "DashboardContainerFactoryDefinition", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerFactoryDefinition", - "text": "DashboardContainerFactoryDefinition" - }, - " implements ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.EmbeddableFactoryDefinition", - "text": "EmbeddableFactoryDefinition" - }, - "<", - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" - }, - ", ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" - }, - ", ", - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainer", - "text": "DashboardContainer" + "path": "src/plugins/dashboard/public/types.ts", + "deprecated": false, + "trackAdoption": false }, - ", unknown>" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.isContainerType", - "type": "boolean", + "id": "def-public.DashboardContainerInput.filters", + "type": "Array", "tags": [], - "label": "isContainerType", + "label": "filters", "description": [], "signature": [ - "true" + "Filter", + "[]" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.type", + "id": "def-public.DashboardContainerInput.title", "type": "string", "tags": [], - "label": "type", + "label": "title", "description": [], - "signature": [ - "\"dashboard\"" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.inject", - "type": "Function", + "id": "def-public.DashboardContainerInput.query", + "type": "Object", "tags": [], - "label": "inject", + "label": "query", "description": [], "signature": [ - "(state: ", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableStateWithType", - "text": "EmbeddableStateWithType" - }, - ", references: ", - "SavedObjectReference", - "[]) => ", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableStateWithType", - "text": "EmbeddableStateWithType" - } + "{ query: string | { [key: string]: any; }; language: string; }" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.inject.$1", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [], - "signature": [ - "P" - ], - "path": "src/plugins/kibana_utils/common/persistable_state/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.inject.$2", - "type": "Array", - "tags": [], - "label": "references", - "description": [], - "signature": [ - "SavedObjectReference", - "[]" - ], - "path": "src/plugins/kibana_utils/common/persistable_state/types.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.extract", - "type": "Function", + "id": "def-public.DashboardContainerInput.panels", + "type": "Object", "tags": [], - "label": "extract", + "label": "panels", "description": [], "signature": [ - "(state: ", + "{ [panelId: string]: ", { - "pluginId": "embeddable", + "pluginId": "dashboard", "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableStateWithType", - "text": "EmbeddableStateWithType" + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" }, - ") => { state: ", + "<", { "pluginId": "embeddable", "scope": "common", "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableStateWithType", - "text": "EmbeddableStateWithType" + "section": "def-common.EmbeddableInput", + "text": "EmbeddableInput" }, - "; references: ", - "SavedObjectReference", - "[]; }" + " & { [k: string]: unknown; }>; }" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.extract.$1", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [], - "signature": [ - "P" - ], - "path": "src/plugins/kibana_utils/common/persistable_state/types.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.Unnamed", - "type": "Function", + "id": "def-public.DashboardContainerInput.executionContext", + "type": "Object", "tags": [], - "label": "Constructor", + "label": "executionContext", "description": [], "signature": [ - "any" + "KibanaExecutionContext", + " | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/public/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "persistableStateService", - "description": [], - "signature": [ - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddablePersistableStateService", - "text": "EmbeddablePersistableStateService" - } - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardFeatureFlagConfig", + "type": "Interface", + "tags": [], + "label": "DashboardFeatureFlagConfig", + "description": [], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardFeatureFlagConfig.allowByValueEmbeddables", + "type": "boolean", + "tags": [], + "label": "allowByValueEmbeddables", + "description": [], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.SavedDashboardPanel", + "type": "Interface", + "tags": [], + "label": "SavedDashboardPanel", + "description": [ + "\nA saved dashboard panel parsed directly from the Dashboard Attributes panels JSON" + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-public.SavedDashboardPanel.embeddableConfig", + "type": "Object", + "tags": [], + "label": "embeddableConfig", + "description": [], + "signature": [ + "{ [key: string]: ", + "Serializable", + "; }" ], - "returnComment": [] + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.isEditable", - "type": "Function", + "id": "def-public.SavedDashboardPanel.id", + "type": "string", "tags": [], - "label": "isEditable", + "label": "id", "description": [], "signature": [ - "() => Promise" + "string | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.SavedDashboardPanel.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.getDisplayName", - "type": "Function", + "id": "def-public.SavedDashboardPanel.panelRefName", + "type": "string", "tags": [], - "label": "getDisplayName", + "label": "panelRefName", "description": [], "signature": [ - "() => string" + "string | undefined" ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.getDefaultInput", - "type": "Function", + "id": "def-public.SavedDashboardPanel.gridData", + "type": "Object", "tags": [], - "label": "getDefaultInput", + "label": "gridData", "description": [], "signature": [ - "() => Partial<", { "pluginId": "dashboard", - "scope": "public", + "scope": "common", "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" - }, - ">" + "section": "def-common.GridData", + "text": "GridData" + } ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.create", - "type": "Function", + "id": "def-public.SavedDashboardPanel.panelIndex", + "type": "string", "tags": [], - "label": "create", + "label": "panelIndex", "description": [], - "signature": [ - "(initialInput: ", - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" - }, - ", parent?: ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.Container", - "text": "Container" - }, - "<{}, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerInput", - "text": "ContainerInput" - }, - "<{}>, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" - }, - "> | undefined) => Promise<", - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainer", - "text": "DashboardContainer" - }, - " | ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ErrorEmbeddable", - "text": "ErrorEmbeddable" - }, - ">" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.create.$1", - "type": "Object", - "tags": [], - "label": "initialInput", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" - } - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerFactoryDefinition.create.$2", - "type": "Object", - "tags": [], - "label": "parent", - "description": [], - "signature": [ - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.Container", - "text": "Container" - }, - "<{}, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerInput", - "text": "ContainerInput" - }, - "<{}>, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" - }, - "> | undefined" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.SavedDashboardPanel.version", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.SavedDashboardPanel.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "signature": [ + "string | undefined" ], - "returnComment": [] + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false } ], - "functions": [ + "enums": [], + "misc": [ { "parentPluginId": "dashboard", - "id": "def-public.cleanEmptyKeys", - "type": "Function", + "id": "def-public.DASHBOARD_CONTAINER_TYPE", + "type": "string", "tags": [], - "label": "cleanEmptyKeys", + "label": "DASHBOARD_CONTAINER_TYPE", "description": [], "signature": [ - "(stateObj: Record) => Record" + "\"dashboard\"" ], - "path": "src/plugins/dashboard/public/locator.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.cleanEmptyKeys.$1", - "type": "Object", - "tags": [], - "label": "stateObj", - "description": [], - "signature": [ - "Record" - ], - "path": "src/plugins/dashboard/public/locator.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "dashboard", - "id": "def-public.createDashboardEditUrl", - "type": "Function", + "id": "def-public.DashboardAppLocator", + "type": "Type", "tags": [], - "label": "createDashboardEditUrl", + "label": "DashboardAppLocator", "description": [], "signature": [ - "(id: string | undefined, editMode: boolean | undefined) => string" - ], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { - "parentPluginId": "dashboard", - "id": "def-public.createDashboardEditUrl.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false + "pluginId": "share", + "scope": "common", + "docId": "kibSharePluginApi", + "section": "def-common.LocatorPublic", + "text": "LocatorPublic" }, + "<", { - "parentPluginId": "dashboard", - "id": "def-public.createDashboardEditUrl.$2", - "type": "CompoundType", - "tags": [], - "label": "editMode", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } + "pluginId": "dashboard", + "scope": "public", + "docId": "kibDashboardPluginApi", + "section": "def-public.DashboardAppLocatorParams", + "text": "DashboardAppLocatorParams" + }, + ">" ], - "returnComment": [], + "path": "src/plugins/dashboard/public/locator.ts", + "deprecated": false, + "trackAdoption": false, "initialIsOpen": false - } - ], - "interfaces": [ + }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput", - "type": "Interface", + "id": "def-public.DashboardAppLocatorParams", + "type": "Type", "tags": [], - "label": "DashboardContainerInput", - "description": [], + "label": "DashboardAppLocatorParams", + "description": [ + "\nWe use `type` instead of `interface` to avoid having to extend this type with\n`SerializableRecord`. See https://github.com/microsoft/TypeScript/issues/15300." + ], "signature": [ + "{ dashboardId?: string | undefined; timeRange?: ", + "TimeRange", + " | undefined; refreshInterval?: ", { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardContainerInput", - "text": "DashboardContainerInput" + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" }, - " extends ", + " | undefined; filters?: ", + "Filter", + "[] | undefined; query?: ", + "Query", + " | undefined; useHash?: boolean | undefined; preserveSavedFilters?: boolean | undefined; viewMode?: ", { "pluginId": "embeddable", - "scope": "public", + "scope": "common", "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerInput", - "text": "ContainerInput" + "section": "def-common.ViewMode", + "text": "ViewMode" }, - "<{}>" + " | undefined; searchSessionId?: string | undefined; panels?: (", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" + }, + " & ", + "SerializableRecord", + ")[] | undefined; savedQuery?: string | undefined; tags?: string[] | undefined; options?: ", + "DashboardOptions", + " | undefined; controlGroupInput?: ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.SerializableControlGroupInput", + "text": "SerializableControlGroupInput" + }, + " | undefined; }" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/locator.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardConstants", + "type": "Object", + "tags": [], + "label": "DashboardConstants", + "description": [], + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.controlGroupInput", - "type": "Object", + "id": "def-public.DashboardConstants.LANDING_PAGE_PATH", + "type": "string", "tags": [], - "label": "controlGroupInput", + "label": "LANDING_PAGE_PATH", "description": [], - "signature": [ - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.PersistableControlGroupInput", - "text": "PersistableControlGroupInput" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.refreshConfig", - "type": "Object", + "id": "def-public.DashboardConstants.CREATE_NEW_DASHBOARD_URL", + "type": "string", "tags": [], - "label": "refreshConfig", + "label": "CREATE_NEW_DASHBOARD_URL", "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.RefreshInterval", - "text": "RefreshInterval" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.isEmbeddedExternally", - "type": "CompoundType", + "id": "def-public.DashboardConstants.VIEW_DASHBOARD_URL", + "type": "string", "tags": [], - "label": "isEmbeddedExternally", + "label": "VIEW_DASHBOARD_URL", "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.isFullScreenMode", - "type": "boolean", + "id": "def-public.DashboardConstants.PRINT_DASHBOARD_URL", + "type": "string", "tags": [], - "label": "isFullScreenMode", + "label": "PRINT_DASHBOARD_URL", "description": [], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.expandedPanelId", + "id": "def-public.DashboardConstants.ADD_EMBEDDABLE_ID", "type": "string", "tags": [], - "label": "expandedPanelId", + "label": "ADD_EMBEDDABLE_ID", "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.timeRange", - "type": "Object", + "id": "def-public.DashboardConstants.ADD_EMBEDDABLE_TYPE", + "type": "string", "tags": [], - "label": "timeRange", + "label": "ADD_EMBEDDABLE_TYPE", "description": [], - "signature": [ - "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.timeslice", - "type": "Object", + "id": "def-public.DashboardConstants.DASHBOARDS_ID", + "type": "string", "tags": [], - "label": "timeslice", + "label": "DASHBOARDS_ID", "description": [], - "signature": [ - "[number, number] | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.timeRestore", - "type": "boolean", + "id": "def-public.DashboardConstants.DASHBOARD_ID", + "type": "string", "tags": [], - "label": "timeRestore", + "label": "DASHBOARD_ID", "description": [], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.description", + "id": "def-public.DashboardConstants.DASHBOARD_SAVED_OBJECT_TYPE", "type": "string", "tags": [], - "label": "description", + "label": "DASHBOARD_SAVED_OBJECT_TYPE", "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.useMargins", - "type": "boolean", + "id": "def-public.DashboardConstants.SEARCH_SESSION_ID", + "type": "string", "tags": [], - "label": "useMargins", + "label": "SEARCH_SESSION_ID", "description": [], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.syncColors", - "type": "CompoundType", + "id": "def-public.DashboardConstants.CHANGE_CHECK_DEBOUNCE", + "type": "number", "tags": [], - "label": "syncColors", + "label": "CHANGE_CHECK_DEBOUNCE", "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.syncTooltips", - "type": "CompoundType", + "id": "def-public.DashboardConstants.CHANGE_APPLY_DEBOUNCE", + "type": "number", "tags": [], - "label": "syncTooltips", + "label": "CHANGE_APPLY_DEBOUNCE", "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/public/dashboard_constants.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + } + ], + "setup": { + "parentPluginId": "dashboard", + "id": "def-public.DashboardSetup", + "type": "Interface", + "tags": [], + "label": "DashboardSetup", + "description": [], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardSetup.locator", + "type": "Object", + "tags": [], + "label": "locator", + "description": [], + "signature": [ + { + "pluginId": "dashboard", + "scope": "public", + "docId": "kibDashboardPluginApi", + "section": "def-public.DashboardAppLocator", + "text": "DashboardAppLocator" + }, + " | undefined" + ], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "dashboard", + "id": "def-public.DashboardStart", + "type": "Interface", + "tags": [], + "label": "DashboardStart", + "description": [], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardStart.getDashboardContainerByValueRenderer", + "type": "Function", + "tags": [], + "label": "getDashboardContainerByValueRenderer", + "description": [], + "signature": [ + "() => React.FC" + ], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardStart.locator", + "type": "Object", + "tags": [], + "label": "locator", + "description": [], + "signature": [ + { + "pluginId": "dashboard", + "scope": "public", + "docId": "kibDashboardPluginApi", + "section": "def-public.DashboardAppLocator", + "text": "DashboardAppLocator" + }, + " | undefined" + ], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardStart.dashboardFeatureFlagConfig", + "type": "Object", + "tags": [], + "label": "dashboardFeatureFlagConfig", + "description": [], + "signature": [ + { + "pluginId": "dashboard", + "scope": "public", + "docId": "kibDashboardPluginApi", + "section": "def-public.DashboardFeatureFlagConfig", + "text": "DashboardFeatureFlagConfig" + } + ], + "path": "src/plugins/dashboard/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "dashboard", + "id": "def-server.findByValueEmbeddables", + "type": "Function", + "tags": [], + "label": "findByValueEmbeddables", + "description": [], + "signature": [ + "(savedObjectClient: Pick<", + "ISavedObjectsRepository", + ", \"find\">, embeddableType: string) => Promise<{ [key: string]: ", + "Serializable", + "; }[]>" + ], + "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.viewMode", - "type": "Enum", + "id": "def-server.findByValueEmbeddables.$1", + "type": "Object", "tags": [], - "label": "viewMode", + "label": "savedObjectClient", "description": [], "signature": [ - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.ViewMode", - "text": "ViewMode" - } + "Pick<", + "ISavedObjectsRepository", + ", \"find\">" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.filters", - "type": "Array", + "id": "def-server.findByValueEmbeddables.$2", + "type": "string", "tags": [], - "label": "filters", + "label": "embeddableType", "description": [], "signature": [ - "Filter", - "[]" + "string" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "dashboard", + "id": "def-server.DashboardPluginSetup", + "type": "Interface", + "tags": [], + "label": "DashboardPluginSetup", + "description": [], + "path": "src/plugins/dashboard/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "dashboard", + "id": "def-server.DashboardPluginStart", + "type": "Interface", + "tags": [], + "label": "DashboardPluginStart", + "description": [], + "path": "src/plugins/dashboard/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "dashboard", + "id": "def-common.convertPanelMapToSavedPanels", + "type": "Function", + "tags": [], + "label": "convertPanelMapToSavedPanels", + "description": [], + "signature": [ + "(panels: ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelMap", + "text": "DashboardPanelMap" + }, + ", version: string) => ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" }, + "[]" + ], + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.title", - "type": "string", + "id": "def-common.convertPanelMapToSavedPanels.$1", + "type": "Object", "tags": [], - "label": "title", + "label": "panels", "description": [], - "path": "src/plugins/dashboard/public/types.ts", + "signature": [ + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelMap", + "text": "DashboardPanelMap" + } + ], + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.query", - "type": "Object", + "id": "def-common.convertPanelMapToSavedPanels.$2", + "type": "string", "tags": [], - "label": "query", + "label": "version", "description": [], "signature": [ - "{ query: string | { [key: string]: any; }; language: string; }" + "string" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.convertPanelStateToSavedDashboardPanel", + "type": "Function", + "tags": [], + "label": "convertPanelStateToSavedDashboardPanel", + "description": [], + "signature": [ + "(panelState: ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" + }, + "<", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.SavedObjectEmbeddableInput", + "text": "SavedObjectEmbeddableInput" }, + ">, version: string) => ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" + } + ], + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.panels", + "id": "def-common.convertPanelStateToSavedDashboardPanel.$1", "type": "Object", "tags": [], - "label": "panels", + "label": "panelState", "description": [], "signature": [ - "{ [panelId: string]: ", - "DashboardPanelState", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" + }, "<", { "pluginId": "embeddable", "scope": "common", "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableInput", - "text": "EmbeddableInput" + "section": "def-common.SavedObjectEmbeddableInput", + "text": "SavedObjectEmbeddableInput" }, - " & { [k: string]: unknown; }>; }" + ">" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardContainerInput.executionContext", - "type": "Object", + "id": "def-common.convertPanelStateToSavedDashboardPanel.$2", + "type": "string", "tags": [], - "label": "executionContext", + "label": "version", "description": [], "signature": [ - "KibanaExecutionContext", - " | undefined" + "string" ], - "path": "src/plugins/dashboard/public/types.ts", + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardFeatureFlagConfig", - "type": "Interface", + "id": "def-common.convertSavedDashboardPanelToPanelState", + "type": "Function", "tags": [], - "label": "DashboardFeatureFlagConfig", + "label": "convertSavedDashboardPanelToPanelState", "description": [], - "path": "src/plugins/dashboard/public/plugin.tsx", + "signature": [ + "(savedDashboardPanel: ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" + }, + ") => ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" + }, + "" + ], + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardFeatureFlagConfig.allowByValueEmbeddables", - "type": "boolean", + "id": "def-common.convertSavedDashboardPanelToPanelState.$1", + "type": "Object", "tags": [], - "label": "allowByValueEmbeddables", + "label": "savedDashboardPanel", "description": [], - "path": "src/plugins/dashboard/public/plugin.tsx", + "signature": [ + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" + } + ], + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject", - "type": "Interface", + "id": "def-common.convertSavedPanelsToPanelMap", + "type": "Function", "tags": [], - "label": "DashboardSavedObject", + "label": "convertSavedPanelsToPanelMap", "description": [], "signature": [ + "(panels?: ", { "pluginId": "dashboard", - "scope": "public", + "scope": "common", "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardSavedObject", - "text": "DashboardSavedObject" + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" }, - " extends ", + "[] | undefined) => ", { - "pluginId": "savedObjects", - "scope": "public", - "docId": "kibSavedObjectsPluginApi", - "section": "def-public.SavedObject", - "text": "SavedObject" - }, - "<", - "SavedObjectAttributes", - ">" + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelMap", + "text": "DashboardPanelMap" + } ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.id", - "type": "string", + "id": "def-common.convertSavedPanelsToPanelMap.$1", + "type": "Array", "tags": [], - "label": "id", + "label": "panels", "description": [], "signature": [ - "string | undefined" + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.SavedDashboardPanel", + "text": "SavedDashboardPanel" + }, + "[] | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/lib/dashboard_panel_converters.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.createExtract", + "type": "Function", + "tags": [], + "label": "createExtract", + "description": [], + "signature": [ + "(persistableStateService: ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddablePersistableStateService", + "text": "EmbeddablePersistableStateService" }, + ") => (state: ", { - "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.timeRestore", - "type": "boolean", - "tags": [], - "label": "timeRestore", - "description": [], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", - "deprecated": false, - "trackAdoption": false + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableStateWithType", + "text": "EmbeddableStateWithType" + }, + ") => { state: ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableStateWithType", + "text": "EmbeddableStateWithType" }, + "; references: ", + "SavedObjectReference", + "[]; }" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.timeTo", - "type": "string", + "id": "def-common.createExtract.$1", + "type": "Object", "tags": [], - "label": "timeTo", + "label": "persistableStateService", "description": [], "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", - "deprecated": false, - "trackAdoption": false - }, + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddablePersistableStateService", + "text": "EmbeddablePersistableStateService" + } + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.createInject", + "type": "Function", + "tags": [], + "label": "createInject", + "description": [], + "signature": [ + "(persistableStateService: ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddablePersistableStateService", + "text": "EmbeddablePersistableStateService" + }, + ") => (state: ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableStateWithType", + "text": "EmbeddableStateWithType" + }, + ", references: ", + "SavedObjectReference", + "[]) => ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableStateWithType", + "text": "EmbeddableStateWithType" + } + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.timeFrom", - "type": "string", + "id": "def-common.createInject.$1", + "type": "Object", "tags": [], - "label": "timeFrom", + "label": "persistableStateService", "description": [], "signature": [ - "string | undefined" + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddablePersistableStateService", + "text": "EmbeddablePersistableStateService" + } + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_container_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.extractReferences", + "type": "Function", + "tags": [], + "label": "extractReferences", + "description": [], + "signature": [ + "({ attributes, references = [] }: SavedObjectAttributesAndReferences, deps: ", + "ExtractDeps", + ") => SavedObjectAttributesAndReferences" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-common.extractReferences.$1", + "type": "Object", + "tags": [], + "label": "{ attributes, references = [] }", + "description": [], + "signature": [ + "SavedObjectAttributesAndReferences" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dashboard", + "id": "def-common.extractReferences.$2", + "type": "Object", + "tags": [], + "label": "deps", + "description": [], + "signature": [ + "ExtractDeps" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.injectReferences", + "type": "Function", + "tags": [], + "label": "injectReferences", + "description": [], + "signature": [ + "({ attributes, references = [] }: SavedObjectAttributesAndReferences, deps: ", + "InjectDeps", + ") => ", + "SavedObjectAttributes" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-common.injectReferences.$1", + "type": "Object", + "tags": [], + "label": "{ attributes, references = [] }", + "description": [], + "signature": [ + "SavedObjectAttributesAndReferences" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dashboard", + "id": "def-common.injectReferences.$2", + "type": "Object", + "tags": [], + "label": "deps", + "description": [], + "signature": [ + "InjectDeps" + ], + "path": "src/plugins/dashboard/common/persistable_state/dashboard_saved_object_references.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes", + "type": "Interface", + "tags": [], + "label": "DashboardAttributes", + "description": [ + "\nThe attributes of the dashboard saved object. This interface should be the\nsource of truth for the latest dashboard attributes shape after all migrations." + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes.controlGroupInput", + "type": "CompoundType", + "tags": [], + "label": "controlGroupInput", + "description": [], + "signature": [ + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, + " | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.description", - "type": "string", + "id": "def-common.DashboardAttributes.refreshInterval", + "type": "Object", "tags": [], - "label": "description", + "label": "refreshInterval", "description": [], "signature": [ - "string | undefined" + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" + }, + " | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.panelsJSON", - "type": "string", + "id": "def-common.DashboardAttributes.timeRestore", + "type": "boolean", "tags": [], - "label": "panelsJSON", + "label": "timeRestore", "description": [], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.optionsJSON", + "id": "def-common.DashboardAttributes.optionsJSON", "type": "string", "tags": [], "label": "optionsJSON", @@ -1710,687 +1581,501 @@ "signature": [ "string | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes.useMargins", + "type": "CompoundType", + "tags": [], + "label": "useMargins", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes.panelsJSON", + "type": "string", + "tags": [], + "label": "panelsJSON", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.uiStateJSON", + "id": "def-common.DashboardAttributes.timeFrom", "type": "string", "tags": [], - "label": "uiStateJSON", + "label": "timeFrom", "description": [], "signature": [ "string | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.lastSavedTitle", - "type": "string", + "id": "def-common.DashboardAttributes.version", + "type": "number", "tags": [], - "label": "lastSavedTitle", + "label": "version", "description": [], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.refreshInterval", - "type": "Object", + "id": "def-common.DashboardAttributes.timeTo", + "type": "string", "tags": [], - "label": "refreshInterval", + "label": "timeTo", "description": [], "signature": [ - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.RefreshInterval", - "text": "RefreshInterval" - }, - " | undefined" + "string | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardAttributes.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.searchSource", + "id": "def-common.DashboardAttributes.kibanaSavedObjectMeta", "type": "Object", "tags": [], - "label": "searchSource", + "label": "kibanaSavedObjectMeta", "description": [], "signature": [ - "{ create: () => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; history: ", - "SearchRequest", - "[]; setOverwriteDataViewType: (overwriteType: string | false | undefined) => void; setField: (field: K, value: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceFields", - "text": "SearchSourceFields" - }, - "[K]) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; removeField: (field: K) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; setFields: (newFields: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceFields", - "text": "SearchSourceFields" - }, - ") => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; getId: () => string; getFields: () => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceFields", - "text": "SearchSourceFields" - }, - "; getField: (field: K, recurse?: boolean) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceFields", - "text": "SearchSourceFields" - }, - "[K]; getActiveIndexFilter: () => string[]; getOwnField: (field: K) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceFields", - "text": "SearchSourceFields" - }, - "[K]; createCopy: () => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; createChild: (options?: {}) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; setParent: (parent?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ISearchSource", - "text": "ISearchSource" - }, - " | undefined, options?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceOptions", - "text": "SearchSourceOptions" - }, - ") => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - "; getParent: () => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - " | undefined; fetch$: (options?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceSearchOptions", - "text": "SearchSourceSearchOptions" - }, - ") => ", - "Observable", - "<", + "{ searchSourceJSON: string; }" + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardContainerStateWithType", + "type": "Interface", + "tags": [], + "label": "DashboardContainerStateWithType", + "description": [ + "--------------------------------------------------------------------\nDashboard container types\n -----------------------------------------------------------------------\n\nTypes below this line are copied here because so many important types are tied up in public. These types should be\nmoved from public into common." + ], + "signature": [ + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardContainerStateWithType", + "text": "DashboardContainerStateWithType" + }, + " extends ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableStateWithType", + "text": "EmbeddableStateWithType" + } + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardContainerStateWithType.panels", + "type": "Object", + "tags": [], + "label": "panels", + "description": [], + "signature": [ + "{ [panelId: string]: ", { - "pluginId": "data", + "pluginId": "dashboard", "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.IKibanaSearchResponse", - "text": "IKibanaSearchResponse" + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" }, "<", - "SearchResponse", - ">>>; fetch: (options?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceSearchOptions", - "text": "SearchSourceSearchOptions" - }, - ") => Promise<", - "SearchResponse", - ">>; onRequestStart: (handler: (searchSource: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSource", - "text": "SearchSource" - }, - ", options?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SearchSourceSearchOptions", - "text": "SearchSourceSearchOptions" - }, - " | undefined) => Promise) => void; getSearchRequestBody: () => any; destroy: () => void; getSerializedFields: (recurse?: boolean) => ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.SerializedSearchSourceFields", - "text": "SerializedSearchSourceFields" - }, - "; serialize: () => { searchSourceJSON: string; references: ", - "SavedObjectReference", - "[]; }; toExpressionAst: ({ asDatatable }?: ExpressionAstOptions) => ", { - "pluginId": "expressions", + "pluginId": "embeddable", "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExpressionAstExpression", - "text": "ExpressionAstExpression" + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableInput", + "text": "EmbeddableInput" }, - "; parseActiveIndexPatternFromQueryString: (queryString: string) => string[]; }" + " & { [k: string]: unknown; }>; }" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.getQuery", - "type": "Function", + "id": "def-common.DashboardContainerStateWithType.controlGroupInput", + "type": "Object", "tags": [], - "label": "getQuery", + "label": "controlGroupInput", "description": [], "signature": [ - "() => ", - "Query" + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + " | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardPanelMap", + "type": "Interface", + "tags": [], + "label": "DashboardPanelMap", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.getFilters", - "type": "Function", + "id": "def-common.DashboardPanelMap.Unnamed", + "type": "IndexSignature", "tags": [], - "label": "getFilters", + "label": "[key: string]: DashboardPanelState", "description": [], "signature": [ - "() => ", - "Filter", - "[]" + "[key: string]: ", + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" + }, + "<", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.SavedObjectEmbeddableInput", + "text": "SavedObjectEmbeddableInput" + }, + ">" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.DashboardPanelState", + "type": "Interface", + "tags": [], + "label": "DashboardPanelState", + "description": [ + "--------------------------------------------------------------------\nDashboard panel types\n -----------------------------------------------------------------------\n\nThe dashboard panel format expected by the embeddable container." + ], + "signature": [ + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.DashboardPanelState", + "text": "DashboardPanelState" }, + " extends ", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.PanelState", + "text": "PanelState" + }, + "" + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.getFullEditPath", - "type": "Function", + "id": "def-common.DashboardPanelState.gridData", + "type": "Object", "tags": [], - "label": "getFullEditPath", + "label": "gridData", "description": [], "signature": [ - "(editMode?: boolean | undefined) => string" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { - "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.getFullEditPath.$1", - "type": "CompoundType", - "tags": [], - "label": "editMode", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.GridData", + "text": "GridData" } ], - "returnComment": [] + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.outcome", - "type": "CompoundType", + "id": "def-common.DashboardPanelState.panelRefName", + "type": "string", "tags": [], - "label": "outcome", + "label": "panelRefName", "description": [], "signature": [ - "\"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined" + "string | undefined" ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dashboard", + "id": "def-common.GridData", + "type": "Interface", + "tags": [], + "label": "GridData", + "description": [ + "\nGrid type for React Grid Layout" + ], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.aliasId", - "type": "string", + "id": "def-common.GridData.w", + "type": "number", "tags": [], - "label": "aliasId", + "label": "w", "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.aliasPurpose", - "type": "CompoundType", + "id": "def-common.GridData.h", + "type": "number", "tags": [], - "label": "aliasPurpose", + "label": "h", "description": [], - "signature": [ - "\"savedObjectConversion\" | \"savedObjectImport\" | undefined" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardSavedObject.controlGroupInput", - "type": "Object", + "id": "def-common.GridData.x", + "type": "number", "tags": [], - "label": "controlGroupInput", + "label": "x", "description": [], - "signature": [ - "Omit<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.RawControlGroupAttributes", - "text": "RawControlGroupAttributes" - }, - ", \"id\"> | undefined" - ], - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], - "enums": [], - "misc": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DASHBOARD_CONTAINER_TYPE", - "type": "string", - "tags": [], - "label": "DASHBOARD_CONTAINER_TYPE", - "description": [], - "signature": [ - "\"dashboard\"" - ], - "path": "src/plugins/dashboard/public/application/embeddable/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardAppLocator", - "type": "Type", - "tags": [], - "label": "DashboardAppLocator", - "description": [], - "signature": [ - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardAppLocatorParams", - "text": "DashboardAppLocatorParams" - }, - ">" - ], - "path": "src/plugins/dashboard/public/locator.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardAppLocatorParams", - "type": "Type", - "tags": [], - "label": "DashboardAppLocatorParams", - "description": [ - "\nWe use `type` instead of `interface` to avoid having to extend this type with\n`SerializableRecord`. See https://github.com/microsoft/TypeScript/issues/15300." - ], - "signature": [ - "{ dashboardId?: string | undefined; timeRange?: ", - "TimeRange", - " | undefined; refreshInterval?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.RefreshInterval", - "text": "RefreshInterval" - }, - " | undefined; filters?: ", - "Filter", - "[] | undefined; query?: ", - "Query", - " | undefined; useHash?: boolean | undefined; preserveSavedFilters?: boolean | undefined; viewMode?: ", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.ViewMode", - "text": "ViewMode" }, - " | undefined; searchSessionId?: string | undefined; panels?: ", { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel730ToLatest", - "text": "SavedDashboardPanel730ToLatest" + "parentPluginId": "dashboard", + "id": "def-common.GridData.y", + "type": "number", + "tags": [], + "label": "y", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false }, - "[] | undefined; savedQuery?: string | undefined; tags?: string[] | undefined; options?: ", - "DashboardOptions", - " | undefined; controlGroupInput?: ", { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.SerializableControlGroupInput", - "text": "SerializableControlGroupInput" - }, - " | undefined; }" + "parentPluginId": "dashboard", + "id": "def-common.GridData.i", + "type": "string", + "tags": [], + "label": "i", + "description": [], + "path": "src/plugins/dashboard/common/types.ts", + "deprecated": false, + "trackAdoption": false + } ], - "path": "src/plugins/dashboard/public/locator.ts", - "deprecated": false, - "trackAdoption": false, "initialIsOpen": false }, { "parentPluginId": "dashboard", - "id": "def-public.SavedDashboardPanel", - "type": "Type", + "id": "def-common.SavedDashboardPanel", + "type": "Interface", "tags": [], "label": "SavedDashboardPanel", "description": [ - "\nThis should always represent the latest dashboard panel shape, after all possible migrations." - ], - "signature": [ - "Pick<", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.RawSavedDashboardPanel730ToLatest", - "text": "RawSavedDashboardPanel730ToLatest" - }, - ", \"type\" | \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" + "\nA saved dashboard panel parsed directly from the Dashboard Attributes panels JSON" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false, - "initialIsOpen": false - } - ], - "objects": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants", - "type": "Object", - "tags": [], - "label": "DashboardConstants", - "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false, "children": [ { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.LANDING_PAGE_PATH", - "type": "string", - "tags": [], - "label": "LANDING_PAGE_PATH", - "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.CREATE_NEW_DASHBOARD_URL", - "type": "string", + "id": "def-common.SavedDashboardPanel.embeddableConfig", + "type": "Object", "tags": [], - "label": "CREATE_NEW_DASHBOARD_URL", + "label": "embeddableConfig", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "signature": [ + "{ [key: string]: ", + "Serializable", + "; }" + ], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.VIEW_DASHBOARD_URL", + "id": "def-common.SavedDashboardPanel.id", "type": "string", "tags": [], - "label": "VIEW_DASHBOARD_URL", + "label": "id", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "signature": [ + "string | undefined" + ], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.PRINT_DASHBOARD_URL", + "id": "def-common.SavedDashboardPanel.type", "type": "string", "tags": [], - "label": "PRINT_DASHBOARD_URL", + "label": "type", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.ADD_EMBEDDABLE_ID", + "id": "def-common.SavedDashboardPanel.panelRefName", "type": "string", "tags": [], - "label": "ADD_EMBEDDABLE_ID", + "label": "panelRefName", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "signature": [ + "string | undefined" + ], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.ADD_EMBEDDABLE_TYPE", - "type": "string", + "id": "def-common.SavedDashboardPanel.gridData", + "type": "Object", "tags": [], - "label": "ADD_EMBEDDABLE_TYPE", + "label": "gridData", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "signature": [ + { + "pluginId": "dashboard", + "scope": "common", + "docId": "kibDashboardPluginApi", + "section": "def-common.GridData", + "text": "GridData" + } + ], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.DASHBOARDS_ID", + "id": "def-common.SavedDashboardPanel.panelIndex", "type": "string", "tags": [], - "label": "DASHBOARDS_ID", + "label": "panelIndex", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.DASHBOARD_ID", + "id": "def-common.SavedDashboardPanel.version", "type": "string", "tags": [], - "label": "DASHBOARD_ID", + "label": "version", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.SEARCH_SESSION_ID", + "id": "def-common.SavedDashboardPanel.title", "type": "string", "tags": [], - "label": "SEARCH_SESSION_ID", - "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.CHANGE_CHECK_DEBOUNCE", - "type": "number", - "tags": [], - "label": "CHANGE_CHECK_DEBOUNCE", - "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardConstants.CHANGE_APPLY_DEBOUNCE", - "type": "number", - "tags": [], - "label": "CHANGE_APPLY_DEBOUNCE", + "label": "title", "description": [], - "path": "src/plugins/dashboard/public/dashboard_constants.ts", + "signature": [ + "string | undefined" + ], + "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, "trackAdoption": false } @@ -2398,674 +2083,8 @@ "initialIsOpen": false } ], - "setup": { - "parentPluginId": "dashboard", - "id": "def-public.DashboardSetup", - "type": "Interface", - "tags": [], - "label": "DashboardSetup", - "description": [], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardSetup.locator", - "type": "Object", - "tags": [], - "label": "locator", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardAppLocator", - "text": "DashboardAppLocator" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - } - ], - "lifecycle": "setup", - "initialIsOpen": true - }, - "start": { - "parentPluginId": "dashboard", - "id": "def-public.DashboardStart", - "type": "Interface", - "tags": [], - "label": "DashboardStart", - "description": [], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardStart.getSavedDashboardLoader", - "type": "Function", - "tags": [], - "label": "getSavedDashboardLoader", - "description": [], - "signature": [ - "() => ", - "SavedObjectLoader" - ], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardStart.getDashboardContainerByValueRenderer", - "type": "Function", - "tags": [], - "label": "getDashboardContainerByValueRenderer", - "description": [], - "signature": [ - "() => React.FC" - ], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardStart.locator", - "type": "Object", - "tags": [], - "label": "locator", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardAppLocator", - "text": "DashboardAppLocator" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-public.DashboardStart.dashboardFeatureFlagConfig", - "type": "Object", - "tags": [], - "label": "dashboardFeatureFlagConfig", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "public", - "docId": "kibDashboardPluginApi", - "section": "def-public.DashboardFeatureFlagConfig", - "text": "DashboardFeatureFlagConfig" - } - ], - "path": "src/plugins/dashboard/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - } - ], - "lifecycle": "start", - "initialIsOpen": true - } - }, - "server": { - "classes": [], - "functions": [ - { - "parentPluginId": "dashboard", - "id": "def-server.findByValueEmbeddables", - "type": "Function", - "tags": [], - "label": "findByValueEmbeddables", - "description": [], - "signature": [ - "(savedObjectClient: Pick<", - "ISavedObjectsRepository", - ", \"find\">, embeddableType: string) => Promise<{ [key: string]: ", - "Serializable", - "; }[]>" - ], - "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-server.findByValueEmbeddables.$1", - "type": "Object", - "tags": [], - "label": "savedObjectClient", - "description": [], - "signature": [ - "Pick<", - "ISavedObjectsRepository", - ", \"find\">" - ], - "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-server.findByValueEmbeddables.$2", - "type": "string", - "tags": [], - "label": "embeddableType", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [], "enums": [], "misc": [], - "objects": [], - "setup": { - "parentPluginId": "dashboard", - "id": "def-server.DashboardPluginSetup", - "type": "Interface", - "tags": [], - "label": "DashboardPluginSetup", - "description": [], - "path": "src/plugins/dashboard/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "lifecycle": "setup", - "initialIsOpen": true - }, - "start": { - "parentPluginId": "dashboard", - "id": "def-server.DashboardPluginStart", - "type": "Interface", - "tags": [], - "label": "DashboardPluginStart", - "description": [], - "path": "src/plugins/dashboard/server/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "lifecycle": "start", - "initialIsOpen": true - } - }, - "common": { - "classes": [], - "functions": [ - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730", - "type": "Function", - "tags": [], - "label": "migratePanelsTo730", - "description": [], - "signature": [ - "(panels: (", - "RawSavedDashboardPanelTo60", - " | ", - "RawSavedDashboardPanel610", - " | ", - "RawSavedDashboardPanel620", - " | ", - "RawSavedDashboardPanel640To720", - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanelTo60", - "text": "SavedDashboardPanelTo60" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel610", - "text": "SavedDashboardPanel610" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel620", - "text": "SavedDashboardPanel620" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel630", - "text": "SavedDashboardPanel630" - }, - ")[], version: string, useMargins: boolean, uiState: { [key: string]: ", - "SerializableRecord", - "; } | undefined) => ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.RawSavedDashboardPanel730ToLatest", - "text": "RawSavedDashboardPanel730ToLatest" - }, - "[]" - ], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730.$1", - "type": "Array", - "tags": [], - "label": "panels", - "description": [], - "signature": [ - "(", - "RawSavedDashboardPanelTo60", - " | ", - "RawSavedDashboardPanel610", - " | ", - "RawSavedDashboardPanel620", - " | ", - "RawSavedDashboardPanel640To720", - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanelTo60", - "text": "SavedDashboardPanelTo60" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel610", - "text": "SavedDashboardPanel610" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel620", - "text": "SavedDashboardPanel620" - }, - " | ", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.SavedDashboardPanel630", - "text": "SavedDashboardPanel630" - }, - ")[]" - ], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730.$2", - "type": "string", - "tags": [], - "label": "version", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730.$3", - "type": "boolean", - "tags": [], - "label": "useMargins", - "description": [], - "signature": [ - "boolean" - ], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730.$4", - "type": "Object", - "tags": [], - "label": "uiState", - "description": [], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.migratePanelsTo730.$4.Unnamed", - "type": "IndexSignature", - "tags": [], - "label": "[key: string]: SerializableRecord", - "description": [], - "signature": [ - "[key: string]: ", - "SerializableRecord" - ], - "path": "src/plugins/dashboard/common/migrate_to_730_panels.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [ - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardContainerStateWithType", - "type": "Interface", - "tags": [], - "label": "DashboardContainerStateWithType", - "description": [], - "signature": [ - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.DashboardContainerStateWithType", - "text": "DashboardContainerStateWithType" - }, - " extends ", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableStateWithType", - "text": "EmbeddableStateWithType" - } - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardContainerStateWithType.panels", - "type": "Object", - "tags": [], - "label": "panels", - "description": [], - "signature": [ - "{ [panelId: string]: ", - "DashboardPanelState", - "<", - { - "pluginId": "embeddable", - "scope": "common", - "docId": "kibEmbeddablePluginApi", - "section": "def-common.EmbeddableInput", - "text": "EmbeddableInput" - }, - " & { [k: string]: unknown; }>; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardContainerStateWithType.controlGroupInput", - "type": "Object", - "tags": [], - "label": "controlGroupInput", - "description": [], - "signature": [ - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.PersistableControlGroupInput", - "text": "PersistableControlGroupInput" - }, - " | undefined" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], - "enums": [], - "misc": [ - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardDoc700To720", - "type": "Type", - "tags": [], - "label": "DashboardDoc700To720", - "description": [], - "signature": [ - "Doc" - ], - "path": "src/plugins/dashboard/common/bwc/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardDoc730ToLatest", - "type": "Type", - "tags": [], - "label": "DashboardDoc730ToLatest", - "description": [], - "signature": [ - "Doc" - ], - "path": "src/plugins/dashboard/common/bwc/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.DashboardDocPre700", - "type": "Type", - "tags": [], - "label": "DashboardDocPre700", - "description": [], - "signature": [ - "DocPre700" - ], - "path": "src/plugins/dashboard/common/bwc/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.GridData", - "type": "Type", - "tags": [], - "label": "GridData", - "description": [], - "signature": [ - "{ w: number; h: number; x: number; y: number; i: string; }" - ], - "path": "src/plugins/dashboard/common/embeddable/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.RawSavedDashboardPanel730ToLatest", - "type": "Type", - "tags": [], - "label": "RawSavedDashboardPanel730ToLatest", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanel640To720", - ", \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly type?: string | undefined; readonly name?: string | undefined; panelIndex: string; panelRefName?: string | undefined; }" - ], - "path": "src/plugins/dashboard/common/bwc/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanel610", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanel610", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanel610", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\"> & { readonly id: string; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanel620", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanel620", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanel620", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanel630", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanel630", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanel620", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanel640To720", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanel640To720", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanel640To720", - ", \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanel730ToLatest", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanel730ToLatest", - "description": [], - "signature": [ - "Pick<", - { - "pluginId": "dashboard", - "scope": "common", - "docId": "kibDashboardPluginApi", - "section": "def-common.RawSavedDashboardPanel730ToLatest", - "text": "RawSavedDashboardPanel730ToLatest" - }, - ", \"type\" | \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.SavedDashboardPanelTo60", - "type": "Type", - "tags": [], - "label": "SavedDashboardPanelTo60", - "description": [], - "signature": [ - "Pick<", - "RawSavedDashboardPanelTo60", - ", \"columns\" | \"title\" | \"sort\" | \"size_x\" | \"size_y\" | \"row\" | \"col\" | \"panelIndex\"> & { readonly id: string; readonly type: string; }" - ], - "path": "src/plugins/dashboard/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], "objects": [ { "parentPluginId": "dashboard", diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 609a9e092533..7a36e763bfea 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 144 | 0 | 139 | 10 | +| 120 | 0 | 113 | 3 | ## Client @@ -37,9 +37,6 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese ### Functions -### Classes - - ### Interfaces @@ -68,6 +65,3 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese ### Interfaces -### Consts, variables and types - - diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index ae6e7c69b3a8..06821933db67 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: 2022-09-28 +date: 2022-09-29 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 b8c91c02caa2..e5c75be5fa09 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.devdocs.json b/api_docs/data_query.devdocs.json index 23849f664136..051270f93955 100644 --- a/api_docs/data_query.devdocs.json +++ b/api_docs/data_query.devdocs.json @@ -1855,14 +1855,6 @@ "plugin": "discover", "path": "src/plugins/discover/public/application/main/services/discover_state.ts" }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/routes/map_page/url_state/global_sync.ts" diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index b81d02840aba..fdd7cb4298da 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 415500b49904..3be213361786 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -2794,9 +2794,9 @@ "label": "options", "description": [], "signature": [ - "{ filter?: any; search?: string | undefined; aggs?: Record | undefined; fields?: string[] | undefined; searchAfter?: string[] | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", + "> | undefined; searchAfter?: string[] | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", "SortOrder", " | undefined; searchFields?: string[] | undefined; rootSearchFields?: string[] | undefined; hasReference?: ", "SavedObjectsFindOptionsReference", @@ -3475,13 +3475,7 @@ "label": "DataRequestHandlerContext", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { search: Promise<", { "pluginId": "data", diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 12e4806b7ea2..619cec4220ea 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 1384a135bfbe..f0d1bf1f9d45 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: 2022-09-28 +date: 2022-09-29 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 4b95039f7981..9800b25ff8ba 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: 2022-09-28 +date: 2022-09-29 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 3608114bd15a..d2f23607c286 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: 2022-09-28 +date: 2022-09-29 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 c6389cc090a7..3c9d9dc38bae 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: 2022-09-28 +date: 2022-09-29 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 69bcfa932c46..86bbfd4e83cf 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index b658b5b79560..547c12c8570c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -24,9 +24,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | alerting, discover, securitySolution | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | | | actions, alerting | - | -| | savedObjects, embeddable, fleet, visualizations, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, dashboard, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | -| | savedObjects, embeddable, fleet, visualizations, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, dashboard, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | -| | discover, dashboard, maps, monitoring | - | +| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | +| | savedObjects, embeddable, fleet, visualizations, dashboard, infra, canvas, graph, securitySolution, actions, alerting, enterpriseSearch, taskManager, savedSearch, ml, @kbn/core-saved-objects-server-internal | - | +| | discover, maps, monitoring | - | | | unifiedSearch, discover, maps, infra, graph, securitySolution, stackAlerts, inputControlVis, savedObjects | - | | | data, discover, embeddable | - | | | advancedSettings, discover | - | @@ -34,7 +34,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | encryptedSavedObjects, actions, data, cloud, ml, logstash, securitySolution | - | | | dashboard, dataVisualizer, stackAlerts, expressionPartitionVis | - | -| | dashboard | - | | | dataViews, maps | - | | | dataViews, maps | - | | | maps | - | @@ -43,6 +42,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement | - | +| | dashboard | - | | | observability, dataVisualizer, fleet, cloudSecurityPosture, discoverEnhanced, osquery, synthetics | - | | | dataViewManagement, dataViews | - | | | dataViews, dataViewManagement | - | @@ -75,10 +75,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | apm, security, securitySolution | 8.8.0 | | | visualizations, dashboard, lens, maps, ml, securitySolution, security, @kbn/core-application-browser-internal, @kbn/core-application-browser-mocks | 8.8.0 | | | securitySolution, @kbn/core-application-browser-internal | 8.8.0 | -| | savedObjectsTaggingOss, dashboard | 8.8.0 | -| | dashboard | 8.8.0 | | | maps, dashboard, @kbn/core-saved-objects-migration-server-internal | 8.8.0 | | | monitoring, kibanaUsageCollection, @kbn/core-apps-browser-internal, @kbn/core-metrics-server-internal, @kbn/core-status-server-internal, @kbn/core-usage-data-server-internal | 8.8.0 | +| | savedObjectsTaggingOss, dashboard | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | @@ -131,6 +130,7 @@ Safe to remove. | | expressions | | | expressions | | | kibanaReact | +| | savedObjects | | | savedObjects | | | licensing | | | licensing | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index dabf6a7945b9..3943c847158f 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -211,16 +211,14 @@ so TS and code-reference navigation might not highlight them. | | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [sync_dashboard_filter_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts#:~:text=syncQueryStateWithUrl), [sync_dashboard_filter_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts#:~:text=syncQueryStateWithUrl) | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/data/types.ts#:~:text=fieldFormats), [data_service.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/data/data_service.ts#:~:text=fieldFormats) | - | | | [dashboard_viewport.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx#:~:text=ExitFullScreenButton), [dashboard_viewport.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx#:~:text=ExitFullScreenButton), [dashboard_viewport.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx#:~:text=ExitFullScreenButton) | - | | | [save_modal.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | -| | [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject) | 8.8.0 | -| | [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObjectClass) | 8.8.0 | +| | [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject) | 8.8.0 | | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/types.ts#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | -| | [dashboard_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts#:~:text=SavedObjectAttributes), [dashboard_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [saved_dashboard_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/saved_dashboard_references.ts#:~:text=SavedObjectAttributes), [saved_dashboard_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/saved_dashboard_references.ts#:~:text=SavedObjectAttributes)+ 8 more | - | -| | [dashboard_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts#:~:text=SavedObjectAttributes), [dashboard_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [saved_dashboard_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/saved_dashboard_references.ts#:~:text=SavedObjectAttributes), [saved_dashboard_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/saved_dashboard_references.ts#:~:text=SavedObjectAttributes)+ 8 more | - | -| | [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning) | 8.8.0 | +| | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes)+ 11 more | - | +| | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes)+ 11 more | - | +| | [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts#:~:text=warning) | 8.8.0 | @@ -525,7 +523,7 @@ so TS and code-reference navigation might not highlight them. | | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [pack_queries_status_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx#:~:text=indexPatternId), [pack_queries_status_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx#:~:text=indexPatternId), [use_discover_link.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/common/hooks/use_discover_link.tsx#:~:text=indexPatternId) | - | +| | [pack_queries_status_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx#:~:text=indexPatternId), [view_results_in_discover.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx#:~:text=indexPatternId), [use_discover_link.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/common/hooks/use_discover_link.tsx#:~:text=indexPatternId) | - | | | [empty_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/components/empty_state.tsx#:~:text=KibanaPageTemplate), [empty_state.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/osquery/public/components/empty_state.tsx#:~:text=KibanaPageTemplate) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 643d002885ef..da0e278bffa6 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -89,10 +89,9 @@ so TS and code-reference navigation might not highlight them. | | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| | dashboard | | [save_modal.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [saved_object_save_modal_dashboard.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx#:~:text=SavedObjectSaveModal), [saved_object_save_modal_dashboard.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | -| dashboard | | [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject) | 8.8.0 | -| dashboard | | [saved_dashboard.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObjectClass) | 8.8.0 | +| dashboard | | [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject) | 8.8.0 | | dashboard | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/types.ts#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | -| dashboard | | [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning) | 8.8.0 | +| dashboard | | [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/saved_objects/migrations/migrate_to_730/migrations_730.ts#:~:text=warning) | 8.8.0 | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index ddeb12beb9a7..b724ab15e695 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 695319775e98..8c4a6308dd17 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -346,7 +346,7 @@ }, { "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx" + "path": "x-pack/plugins/osquery/public/discover/view_results_in_discover.tsx" }, { "plugin": "osquery", @@ -1101,7 +1101,7 @@ "label": "sharingSavedObjectProps", "description": [], "signature": [ - "{ outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" + "{ outcome?: \"conflict\" | \"exactMatch\" | \"aliasMatch\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" ], "path": "src/plugins/saved_search/public/services/saved_searches/types.ts", "deprecated": false, diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 4a2a4abc7d5e..39b2814d4136 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: 2022-09-28 +date: 2022-09-29 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 104fc1d5cde6..e9974fbb2bf4 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 3203b4c95754..8bbe0a063796 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: 2022-09-28 +date: 2022-09-29 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 e5e64a70e801..c53b6a4aba7e 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: 2022-09-28 +date: 2022-09-29 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 6d488adbda83..6cc990a42eba 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: 2022-09-28 +date: 2022-09-29 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 2d3bbbfe7ad8..8f80e15d3886 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index e6eb098f3c9c..6a4ca6fcb773 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 54a7269ef532..159a2f7167b0 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index f821ced3a76e..8361059c9107 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1312,7 +1312,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; created?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" + "(Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false, @@ -1332,7 +1332,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; created?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1347,7 +1347,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; created?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" + "Readonly<{ error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ name?: string | undefined; description?: string | undefined; category?: string | undefined; id?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; category?: string[] | undefined; type?: string[] | undefined; id?: string | undefined; created?: string | undefined; outcome?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: string | number | undefined; code?: string | undefined; url?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: string | number | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index dff00025d47a..b9be569d3a3e 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index ba5e5e742f4c..6d9a8d244589 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: 2022-09-28 +date: 2022-09-29 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 331af14bbd78..b38b786ca55b 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: 2022-09-28 +date: 2022-09-29 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 36ea696e5335..9d856b882a31 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: 2022-09-28 +date: 2022-09-29 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 97052c63f747..84225e80ad00 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: 2022-09-28 +date: 2022-09-29 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 95950d42e447..e6902356f091 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: 2022-09-28 +date: 2022-09-29 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 ee6b2d7e353f..60a4d843aad3 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: 2022-09-28 +date: 2022-09-29 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 c1edcc43088d..85c6421d292d 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: 2022-09-28 +date: 2022-09-29 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 672e12f360d3..4d4a23fe3a6f 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: 2022-09-28 +date: 2022-09-29 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 7fbd70637ed3..ff4af965c963 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: 2022-09-28 +date: 2022-09-29 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 09f5c5583463..2d3ede34cf14 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: 2022-09-28 +date: 2022-09-29 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 8414051020d0..114bb0846272 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: 2022-09-28 +date: 2022-09-29 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 42b6c0963d95..6a87768ff01e 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 340a2de8e8e2..bf0c8f9c49ae 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.devdocs.json b/api_docs/expressions.devdocs.json index cf0d00e1dd47..bae5e63702f4 100644 --- a/api_docs/expressions.devdocs.json +++ b/api_docs/expressions.devdocs.json @@ -11507,7 +11507,7 @@ "\nThis type represents the `type` of any `DatatableColumn` in a `Datatable`.\nits duplicated from KBN_FIELD_TYPES" ], "signature": [ - "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"conflict\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"histogram\"" + "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"conflict\" | \"histogram\"" ], "path": "src/plugins/expressions/common/expression_types/specs/datatable.ts", "deprecated": false, @@ -21037,7 +21037,7 @@ "\nThis type represents the `type` of any `DatatableColumn` in a `Datatable`.\nits duplicated from KBN_FIELD_TYPES" ], "signature": [ - "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"conflict\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"histogram\"" + "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"conflict\" | \"histogram\"" ], "path": "src/plugins/expressions/common/expression_types/specs/datatable.ts", "deprecated": false, @@ -29210,7 +29210,7 @@ "label": "type", "description": [], "signature": [ - "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"conflict\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"histogram\"" + "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"conflict\" | \"histogram\"" ], "path": "src/plugins/expressions/common/expression_types/specs/datatable.ts", "deprecated": false, @@ -35226,7 +35226,7 @@ "\nThis type represents the `type` of any `DatatableColumn` in a `Datatable`.\nits duplicated from KBN_FIELD_TYPES" ], "signature": [ - "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"conflict\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"histogram\"" + "\"string\" | \"number\" | \"boolean\" | \"object\" | \"date\" | \"null\" | \"ip\" | \"nested\" | \"_source\" | \"attachment\" | \"geo_point\" | \"geo_shape\" | \"murmur3\" | \"unknown\" | \"conflict\" | \"histogram\"" ], "path": "src/plugins/expressions/common/expression_types/specs/datatable.ts", "deprecated": false, diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 74c3fd828f44..cb5617b237f7 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: 2022-09-28 +date: 2022-09-29 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 7b4880c40f8d..1c1d57aa91eb 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: 2022-09-28 +date: 2022-09-29 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 8c4ebd13fe23..dc89d286ddfe 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index c0963ced99a2..f29f32174d12 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: 2022-09-28 +date: 2022-09-29 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 96ec1e67bf2b..3cc10c4a1612 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index d068e061e494..3059a8f9c9f8 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -7170,13 +7170,7 @@ "text": "NewPackagePolicy" }, ", context: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ", request: ", "KibanaRequest", ") => Promise
    ) => Promise<", @@ -7611,13 +7593,7 @@ "label": "context", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } + "RequestHandlerContext" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -7709,13 +7685,7 @@ "text": "PackagePolicy" }, ", context: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ", request: ", "KibanaRequest", ") => Promise<", @@ -7761,13 +7731,7 @@ "label": "context", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } + "RequestHandlerContext" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -7808,13 +7772,7 @@ "text": "UpdatePackagePolicy" }, ", context: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", ", request: ", "KibanaRequest", ") => Promise<", @@ -7860,13 +7818,7 @@ "label": "context", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } + "RequestHandlerContext" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -15292,7 +15244,7 @@ "label": "PackageSpecCategory", "description": [], "signature": [ - "\"custom\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\"" + "\"custom\" | \"aws\" | \"azure\" | \"cloud\" | \"config_management\" | \"containers\" | \"crm\" | \"datastore\" | \"elastic_stack\" | \"google_cloud\" | \"infrastructure\" | \"kubernetes\" | \"languages\" | \"message_queue\" | \"monitoring\" | \"network\" | \"notification\" | \"os_system\" | \"productivity\" | \"security\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"web\"" ], "path": "x-pack/plugins/fleet/common/types/models/package_spec.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 6f966dbb5a31..7c5beab0d5c9 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 63433541ece2..6803fa9d095e 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: 2022-09-28 +date: 2022-09-29 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 c28c8b2fef87..062ffb1effb3 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: 2022-09-28 +date: 2022-09-29 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 8c285e037a35..8f772016026c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 2d1523f302cb..29de31f6cf68 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: 2022-09-28 +date: 2022-09-29 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 46b6303673ab..3dac89fa09d7 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index d86809062de0..315978011a0b 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 0a75d4c550c4..1eec7a67db10 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 1b2ce0d5a84c..ba3797cab67d 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 3ca0d95b596d..c6dcd6bde783 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 7e469da7a40b..78c88adbe04a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 9e1d8daed9b7..8e0003a6394a 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 294c0b5ebb04..b936fa218da1 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index e991378eefb6..480d1389d1d5 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 61ec0f495851..67d5f65e938a 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index af69ccaa6645..2c75bd5664b6 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index c80fb067196d..af39931531e6 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index c8fd4ef97940..5515fe767880 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 102c627e9ef5..d85797746210 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 88ae4fc21aca..ec16bbb3798a 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: 2022-09-28 +date: 2022-09-29 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 951b6896fa52..cd86d11a33dc 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index a679b1a252da..24e76f781315 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index c330c66a3b51..a38a76d26da3 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 4cf5662b020c..96f35a5ca85a 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: 2022-09-28 +date: 2022-09-29 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 4396f3ffe02e..efc5f97cca9f 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: 2022-09-28 +date: 2022-09-29 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 1857d82ee013..7030b63abaa1 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: 2022-09-28 +date: 2022-09-29 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.devdocs.json b/api_docs/kbn_ci_stats_reporter.devdocs.json index f831ac502a93..18389c93d07c 100644 --- a/api_docs/kbn_ci_stats_reporter.devdocs.json +++ b/api_docs/kbn_ci_stats_reporter.devdocs.json @@ -654,7 +654,7 @@ "\nOverall result of this test group" ], "signature": [ - "\"fail\" | \"pass\" | \"skip\"" + "\"skip\" | \"fail\" | \"pass\"" ], "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", "deprecated": false, @@ -755,7 +755,7 @@ "\n\"fail\", \"pass\" or \"skip\", the result of the tests" ], "signature": [ - "\"fail\" | \"pass\" | \"skip\"" + "\"skip\" | \"fail\" | \"pass\"" ], "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", "deprecated": false, @@ -1041,7 +1041,7 @@ "label": "CiStatsTestResult", "description": [], "signature": [ - "\"fail\" | \"pass\" | \"skip\"" + "\"skip\" | \"fail\" | \"pass\"" ], "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", "deprecated": false, diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index f3779a0488fb..464ef1f62934 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: 2022-09-28 +date: 2022-09-29 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 e2d60d46020a..7aac22dfeead 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index e0475bc85ecd..edd9cb125e75 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: 2022-09-28 +date: 2022-09-29 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 32514d096d20..b82dc689dba2 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: 2022-09-28 +date: 2022-09-29 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 b93c70c8a0eb..cebca9163e79 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: 2022-09-28 +date: 2022-09-29 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 78d758c0f2fe..bd788d6e1868 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 812ca543dea5..ac56135aa292 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index c29081c8b626..eefeafb88d5d 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: 2022-09-28 +date: 2022-09-29 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 739c7e7af596..36b99e421559 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: 2022-09-28 +date: 2022-09-29 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 2c0999666ff1..abf361ac547b 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: 2022-09-28 +date: 2022-09-29 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 f58a16ef732a..ec45043e4726 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: 2022-09-28 +date: 2022-09-29 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 ced023377219..04e49781988c 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: 2022-09-28 +date: 2022-09-29 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 cbea4bff1bf8..126ab218962c 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: 2022-09-28 +date: 2022-09-29 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 b1580098a365..71e7fae504f5 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: 2022-09-28 +date: 2022-09-29 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 f704df4a7f34..55beb31993bb 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: 2022-09-28 +date: 2022-09-29 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 4767d8fa5053..c777505da45d 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: 2022-09-28 +date: 2022-09-29 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 7c6ce1341617..10828fe0de71 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: 2022-09-28 +date: 2022-09-29 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 17402d105d5b..978ac4faed76 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: 2022-09-28 +date: 2022-09-29 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 48cb4145874c..16fd9445d124 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: 2022-09-28 +date: 2022-09-29 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 28228f4c7476..cdaf427d6802 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: 2022-09-28 +date: 2022-09-29 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 09afb9dd896c..5bd2a717b5b8 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: 2022-09-28 +date: 2022-09-29 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 bf845366f8e6..76f931b4d756 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: 2022-09-28 +date: 2022-09-29 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 bf1d2e6b2545..77aa19a6800c 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: 2022-09-28 +date: 2022-09-29 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 a2eb49783d4b..e6bfe1ecd7db 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: 2022-09-28 +date: 2022-09-29 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 aa1d950326fc..e9475861b697 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: 2022-09-28 +date: 2022-09-29 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 3d8690f8d6f7..d6dc431b07fd 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: 2022-09-28 +date: 2022-09-29 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 78563e898c03..fdf7cc31d41f 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index 4b44cbfe0757..73d538523658 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: 2022-09-28 +date: 2022-09-29 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 f8052fad772a..a1f512749d43 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: 2022-09-28 +date: 2022-09-29 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 79da95fae9e6..42574e3029ea 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: 2022-09-28 +date: 2022-09-29 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index ad16ad6aea2e..6bb0bb64c5ba 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: 2022-09-28 +date: 2022-09-29 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 7fe5dba92aa5..938d8d6b38af 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: 2022-09-28 +date: 2022-09-29 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 2547deec251f..9d33c64bd7f4 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index d96ff821c436..7f254dda6eee 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 74dee4cd3fdc..527a5f2be91e 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: 2022-09-28 +date: 2022-09-29 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 da6a21361ea2..7b470aeff355 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: 2022-09-28 +date: 2022-09-29 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 617a402d35fc..34e0e5fb4d1f 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: 2022-09-28 +date: 2022-09-29 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 1217a7b8fa20..97c4d2e6d4b4 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: 2022-09-28 +date: 2022-09-29 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 0ccaad98b392..0347b3f309a8 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: 2022-09-28 +date: 2022-09-29 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 14eccd7c10c3..5cccdf33b503 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: 2022-09-28 +date: 2022-09-29 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 f5f017918ee4..e2c4d921a70b 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: 2022-09-28 +date: 2022-09-29 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 477917148b69..1d786ddcd349 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: 2022-09-28 +date: 2022-09-29 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 399790680e06..25079df745e3 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: 2022-09-28 +date: 2022-09-29 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 318e2f19b36d..a5712d10acd3 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: 2022-09-28 +date: 2022-09-29 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 b4e402b1d85a..2b2107d52082 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: 2022-09-28 +date: 2022-09-29 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 1263eb43812b..c9aa728e95fb 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: 2022-09-28 +date: 2022-09-29 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 efd3c4dce7d8..630ee26b5024 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: 2022-09-28 +date: 2022-09-29 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 4b8b92e29d47..62ed3d4b8afa 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: 2022-09-28 +date: 2022-09-29 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 12f2b1b89aaa..1c33ab738b0e 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: 2022-09-28 +date: 2022-09-29 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 22703ff1d56c..8d4402a7a2cd 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: 2022-09-28 +date: 2022-09-29 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 58d7084730b0..5e923792bc93 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: 2022-09-28 +date: 2022-09-29 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 9f241f79f309..f2336d9fdfb0 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: 2022-09-28 +date: 2022-09-29 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 2d3ce9691dbc..61e72d43c392 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: 2022-09-28 +date: 2022-09-29 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 db3e364e96ff..1c31d922fc68 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: 2022-09-28 +date: 2022-09-29 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 cbeb87f2524b..00772d47658c 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: 2022-09-28 +date: 2022-09-29 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 e838dfc832fd..7a8edbe3e3b3 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: 2022-09-28 +date: 2022-09-29 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 6bdcb4cd9e37..6dd6b8aebce6 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: 2022-09-28 +date: 2022-09-29 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index afb5974de9a1..8014dbb2ceb6 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: 2022-09-28 +date: 2022-09-29 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 8e28ebbe2ff1..ed565e7b8f3b 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: 2022-09-28 +date: 2022-09-29 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 463b56f36429..632f5546a721 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: 2022-09-28 +date: 2022-09-29 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 bf4cf64d7a58..92184cbab278 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: 2022-09-28 +date: 2022-09-29 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 4047f8a6f484..52f62aad5c93 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: 2022-09-28 +date: 2022-09-29 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.devdocs.json b/api_docs/kbn_core_http_request_handler_context_server.devdocs.json new file mode 100644 index 000000000000..ebf6567c0a8b --- /dev/null +++ b/api_docs/kbn_core_http_request_handler_context_server.devdocs.json @@ -0,0 +1,283 @@ +{ + "id": "@kbn/core-http-request-handler-context-server", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CoreRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "CoreRequestHandlerContext", + "description": [ + "\nThe `core` context provided to route handler.\n\nProvides the following clients and services:\n - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client\n which uses the credentials of the incoming request\n - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing\n all the registered types.\n - {@link IScopedClusterClient | elasticsearch.client} - Elasticsearch\n data client which uses the credentials of the incoming request\n - {@link IUiSettingsClient | uiSettings.client} - uiSettings client\n which uses the credentials of the incoming request" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CoreRequestHandlerContext.savedObjects", + "type": "Object", + "tags": [], + "label": "savedObjects", + "description": [], + "signature": [ + "SavedObjectsRequestHandlerContext" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CoreRequestHandlerContext.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + "ElasticsearchRequestHandlerContext" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CoreRequestHandlerContext.uiSettings", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + "UiSettingsRequestHandlerContext" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CoreRequestHandlerContext.deprecations", + "type": "Object", + "tags": [], + "label": "deprecations", + "description": [], + "signature": [ + "DeprecationsRequestHandlerContext" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootCoreRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "PrebootCoreRequestHandlerContext", + "description": [], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootCoreRequestHandlerContext.uiSettings", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.PrebootUiSettingsRequestHandlerContext", + "text": "PrebootUiSettingsRequestHandlerContext" + } + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "PrebootRequestHandlerContext", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.PrebootRequestHandlerContext", + "text": "PrebootRequestHandlerContext" + }, + " extends ", + "RequestHandlerContextBase" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootRequestHandlerContext.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + "Promise<", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.PrebootCoreRequestHandlerContext", + "text": "PrebootCoreRequestHandlerContext" + }, + ">" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootUiSettingsRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "PrebootUiSettingsRequestHandlerContext", + "description": [], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.PrebootUiSettingsRequestHandlerContext.client", + "type": "Object", + "tags": [], + "label": "client", + "description": [], + "signature": [ + "IUiSettingsClient" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/preboot_request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.RequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "RequestHandlerContext", + "description": [ + "\nBase context passed to a route handler, containing the `core` context part.\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " extends ", + "RequestHandlerContextBase" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.RequestHandlerContext.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + "Promise<", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.CoreRequestHandlerContext", + "text": "CoreRequestHandlerContext" + }, + ">" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-http-request-handler-context-server", + "id": "def-server.CustomRequestHandlerContext", + "type": "Type", + "tags": [], + "label": "CustomRequestHandlerContext", + "description": [ + "\nMixin allowing plugins to define their own request handler contexts.\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "server", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; }" + ], + "path": "packages/core/http/core-http-request-handler-context-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx new file mode 100644 index 000000000000..0e41263673dd --- /dev/null +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -0,0 +1,33 @@ +--- +#### +#### 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: kibKbnCoreHttpRequestHandlerContextServerPluginApi +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: 2022-09-29 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] +--- +import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; + + + +Contact Kibana Core for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 14 | 0 | 11 | 0 | + +## Server + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 650426f49357..0fdb1bfe4318 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 0df0a232ed7e..b758dea2dd26 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_http_server.mdx index 404f13d3ca12..bb3c7c1bd350 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 524b236008cf..220d5282e1fc 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 94a0469ff107..ed120c45d977 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: 2022-09-28 +date: 2022-09-29 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 aac9e17df421..9cb11bc93a1f 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: 2022-09-28 +date: 2022-09-29 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 e83f6e8553d8..1bb71320adbc 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: 2022-09-28 +date: 2022-09-29 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 a89d07338f78..edbbec7b5aac 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: 2022-09-28 +date: 2022-09-29 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 3575cc0d18a9..408a8656056a 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: 2022-09-28 +date: 2022-09-29 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 ce24eb49860c..549c463617a5 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 92e9d0e07cfe..11a4ba2c541e 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 17e4abfa2a1a..579e9087903c 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: 2022-09-28 +date: 2022-09-29 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 d8c9f151e2f4..c88718d0e930 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: 2022-09-28 +date: 2022-09-29 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 574ff5014691..6be7ee221009 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: 2022-09-28 +date: 2022-09-29 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 62c8d4f2358e..863f69b31518 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: 2022-09-28 +date: 2022-09-29 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 8deee157e3d4..add898c16abe 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: 2022-09-28 +date: 2022-09-29 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index cf1126fe73e2..a488e40d01dd 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: 2022-09-28 +date: 2022-09-29 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 bc4d09292edb..87082d1e3319 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: 2022-09-28 +date: 2022-09-29 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 485e124d97e4..7a746c627805 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: 2022-09-28 +date: 2022-09-29 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 4b17dbaac8c8..a413d283cb4c 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: 2022-09-28 +date: 2022-09-29 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 36c1e8c0f9a4..3ad9c040ab8f 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: 2022-09-28 +date: 2022-09-29 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 a1a8ce47aed5..4e9e0445e3f7 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: 2022-09-28 +date: 2022-09-29 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 6b4a55f82676..0cfd64309de9 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: 2022-09-28 +date: 2022-09-29 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 d1494db23637..40f97497e1cf 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: 2022-09-28 +date: 2022-09-29 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 ad35d06977cc..3e4d25261b36 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: 2022-09-28 +date: 2022-09-29 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 5b5b8a942141..9ec6da0be76f 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: 2022-09-28 +date: 2022-09-29 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 bf95da355c9e..e1be058f6694 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: 2022-09-28 +date: 2022-09-29 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 3a07f7a67606..2f8dd3d2d3cf 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: 2022-09-28 +date: 2022-09-29 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 130bddee31d1..99c39b3ae3bf 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: 2022-09-28 +date: 2022-09-29 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 fef5e6c2f9a8..b1abfcae9d1d 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: 2022-09-28 +date: 2022-09-29 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 724251e5e502..6af7ea7a401a 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: 2022-09-28 +date: 2022-09-29 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 3837e623f29c..4e53620f210c 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: 2022-09-28 +date: 2022-09-29 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 0f7f597d06d3..3cdf63fa196b 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: 2022-09-28 +date: 2022-09-29 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 42846c64f33d..ed9822ce6524 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: 2022-09-28 +date: 2022-09-29 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 a52c3697e57d..0e35863807e9 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: 2022-09-28 +date: 2022-09-29 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 238252f104b3..e8513df53c90 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: 2022-09-28 +date: 2022-09-29 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index d5b97eb1a0cb..a33965c6f024 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: 2022-09-28 +date: 2022-09-29 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 20a753b66ea9..499f42fd91b2 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: 2022-09-28 +date: 2022-09-29 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 c074d0f80cb8..cedfc5029275 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: 2022-09-28 +date: 2022-09-29 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_saved_objects_api_browser.devdocs.json b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json index b12a698b2160..bc6024dea788 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json @@ -76,7 +76,7 @@ "\nThe outcome for a successful `resolve` call is one of the following values:\n\n * `'exactMatch'` -- One document exactly matched the given ID.\n * `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different\n than the given ID.\n * `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the\n `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID." ], "signature": [ - "\"exactMatch\" | \"aliasMatch\" | \"conflict\"" + "\"conflict\" | \"exactMatch\" | \"aliasMatch\"" ], "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/apis/resolve.ts", "deprecated": false, @@ -1974,9 +1974,9 @@ "label": "SavedObjectsFindOptions", "description": [], "signature": [ - "{ type: string | string[]; filter?: any; search?: string | undefined; aggs?: Record | undefined; fields?: string[] | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; searchFields?: string[] | undefined; hasReference?: ", + "> | undefined; page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; searchFields?: string[] | undefined; hasReference?: ", "SavedObjectsFindOptionsReference", " | ", "SavedObjectsFindOptionsReference", diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 949af62592ec..de5b9aa205ca 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: 2022-09-28 +date: 2022-09-29 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.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 0e040b72d2d3..1bff25681bdb 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -5949,7 +5949,7 @@ "\nThe outcome for a successful `resolve` call is one of the following values:\n\n * `'exactMatch'` -- One document exactly matched the given ID.\n * `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different\n than the given ID.\n * `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the\n `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID." ], "signature": [ - "\"exactMatch\" | \"aliasMatch\" | \"conflict\"" + "\"conflict\" | \"exactMatch\" | \"aliasMatch\"" ], "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts", "deprecated": false, @@ -6427,9 +6427,9 @@ "label": "SavedObjectsCreatePointInTimeFinderOptions", "description": [], "signature": [ - "{ type: string | string[]; filter?: any; search?: string | undefined; aggs?: Record | undefined; fields?: string[] | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", + "> | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: ", "SortOrder", " | undefined; searchFields?: string[] | undefined; rootSearchFields?: string[] | undefined; hasReference?: ", { diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 6fa09c36e31f..7628f073dc94 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: 2022-09-28 +date: 2022-09-29 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 7c588cdc511e..a91351cf9f73 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 447e323f6f6c..fa562b3b2acf 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: 2022-09-28 +date: 2022-09-29 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 9931b55fa4f2..acc5f724b1bc 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: 2022-09-28 +date: 2022-09-29 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 deb24928a107..114ad87c44b3 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: 2022-09-28 +date: 2022-09-29 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 3bfa422bdff9..6731e2111977 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: 2022-09-28 +date: 2022-09-29 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 1a306a18a3eb..276569792284 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: 2022-09-28 +date: 2022-09-29 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 a34e632cf249..9f0c9ea82ba6 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: 2022-09-28 +date: 2022-09-29 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 59d4d1841c7f..bc3ea822a942 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: 2022-09-28 +date: 2022-09-29 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 f0610e31ec80..b5f3e61df18b 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: 2022-09-28 +date: 2022-09-29 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 cd9ba3c71908..99e95bfdab28 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: 2022-09-28 +date: 2022-09-29 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 f2dd4437d43e..5edf8cbfddef 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: 2022-09-28 +date: 2022-09-29 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 88c329e9ae34..5ed2a4976d1c 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: 2022-09-28 +date: 2022-09-29 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 df7eaa590f6d..8f12d48e0f7a 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: 2022-09-28 +date: 2022-09-29 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 5534b4652be5..6de1b336be40 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: 2022-09-28 +date: 2022-09-29 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 ccf532e33ba3..4fb52fecf871 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: 2022-09-28 +date: 2022-09-29 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 5b78e21ba58c..71fdb228446e 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: 2022-09-28 +date: 2022-09-29 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index f4f9cc899056..49314722d46a 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: 2022-09-28 +date: 2022-09-29 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 86d04a0b4ff7..266265f2ee05 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: 2022-09-28 +date: 2022-09-29 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 52e756da1a55..8d222376f5bf 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: 2022-09-28 +date: 2022-09-29 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 432a7d95007e..f8cae5c4a550 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: 2022-09-28 +date: 2022-09-29 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 8ccca542c4b7..0dacdfd5af97 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: 2022-09-28 +date: 2022-09-29 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 ab86530abfbb..99b4036abb6b 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: 2022-09-28 +date: 2022-09-29 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 9de9031c75db..549cdfe3a58e 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: 2022-09-28 +date: 2022-09-29 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 9c59e5a04b8e..c429de87e13d 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: 2022-09-28 +date: 2022-09-29 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 02d479962f92..0888bc8875a2 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 71eada8e5603..254373b7a14d 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: 2022-09-28 +date: 2022-09-29 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 b8b744e259c5..61ceae147d70 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: 2022-09-28 +date: 2022-09-29 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 a89cdf92e9ac..9b9365bc3081 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: 2022-09-28 +date: 2022-09-29 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 290774ac1f9e..baf48b87a0eb 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: 2022-09-28 +date: 2022-09-29 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 004e65223547..1aba4f31960a 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: 2022-09-28 +date: 2022-09-29 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 84190ae50348..b2fe92199314 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: 2022-09-28 +date: 2022-09-29 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 2f362f1ede3f..91c104b8b9de 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: 2022-09-28 +date: 2022-09-29 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 e214233f02eb..31ba42e2554f 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index b8ac7249cb2a..a25b0cfc501c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 4f5cca38779c..101431a9c73d 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: 2022-09-28 +date: 2022-09-29 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index bc10003b1203..710a3c62d544 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: 2022-09-28 +date: 2022-09-29 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_crypto.mdx b/api_docs/kbn_crypto.mdx index d95c8ad7295e..a8c5ebefe66c 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: 2022-09-28 +date: 2022-09-29 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 06ce6dd99935..b7a8b4187db8 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 4bcac88980eb..77c10c205e81 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 42846a27d0d5..f79bd78824a0 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: 2022-09-28 +date: 2022-09-29 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 5db4302dd4cd..156f2ad1e34d 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: 2022-09-28 +date: 2022-09-29 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 968955efd32a..c40a114fb200 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: 2022-09-28 +date: 2022-09-29 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 e047906625c2..6c817ebf71e7 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index c7d4d0fe3d26..055b95affc24 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: 2022-09-28 +date: 2022-09-29 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 30e9b543d9d8..f6a0a8c29e49 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 8590060b0796..fa63acb9d852 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 1e69af86cfda..9f608a801f2c 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: 2022-09-28 +date: 2022-09-29 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 695ed989dfe9..1498ddb5d263 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: 2022-09-28 +date: 2022-09-29 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 b329278d8272..537db266a40f 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: 2022-09-28 +date: 2022-09-29 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 9a2cfbe08a28..923c644404d7 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: 2022-09-28 +date: 2022-09-29 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 2e0b9742678d..23893848967a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 0373e71bbf94..a29e11c7f3f5 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index f0a54f4a6740..c3673495d2d9 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: 2022-09-28 +date: 2022-09-29 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index a2bfedd08171..29968a934531 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: 2022-09-28 +date: 2022-09-29 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_generate.mdx b/api_docs/kbn_generate.mdx index 2a9f62b4f3f3..535192fd2288 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index b1b28470de32..270341543e97 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index b675d4d8f4da..75d249e64bf7 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: 2022-09-28 +date: 2022-09-29 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 b7abe8b51295..d7346ce1b677 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index e7a5d6fe0f7e..b1ce2043e27f 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: 2022-09-28 +date: 2022-09-29 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 f716a25e4408..63510d74b009 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: 2022-09-28 +date: 2022-09-29 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 25ba31dee6a6..ce7de735a110 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index c36a372e85d7..a96adba4a09b 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 6051ff9b96e1..d35da6c7ae88 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 0554879f31ad..623552a19ab9 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index c005d1ea66f6..46b40beb8bf3 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: 2022-09-28 +date: 2022-09-29 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 b2cd34e94a50..e9b4b290f7d1 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 1b91d601c224..3f19d6bc0c35 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 13444331d9fc..da43bfec51c4 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: 2022-09-28 +date: 2022-09-29 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 d6db9caa5dd0..2770d7ccb6ac 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 12ab660fc176..83b9321c6a3f 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index a0fbd8c5ef3c..ae4854e96ac2 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 6808dca6dbe3..dd5f5c5ace18 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: 2022-09-28 +date: 2022-09-29 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 8da4778d8b81..4a750b0b7dfa 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: 2022-09-28 +date: 2022-09-29 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index e9cf6c1838c5..170269826262 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 4ec4c4ef7c9e..987ec45f387a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 757c4018832a..ed7566b575b5 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: 2022-09-28 +date: 2022-09-29 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 11961ef3d602..7c45eeef92b5 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: 2022-09-28 +date: 2022-09-29 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 a980b80bd7a3..859b6a067644 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: 2022-09-28 +date: 2022-09-29 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index b1051ed927c8..aedd0101a45a 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: 2022-09-28 +date: 2022-09-29 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 98d5196dc24f..bfd4b4e6b5af 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: 2022-09-28 +date: 2022-09-29 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 a04f5630c37b..3ca9f9e155f1 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index af33c58333e7..293fa5190545 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 2a373248eac9..aaf5d45ea057 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.devdocs.json b/api_docs/kbn_rule_data_utils.devdocs.json index c30477647a1c..2d6403bd41df 100644 --- a/api_docs/kbn_rule_data_utils.devdocs.json +++ b/api_docs/kbn_rule_data_utils.devdocs.json @@ -912,7 +912,7 @@ "label": "AlertConsumers", "description": [], "signature": [ - "\"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\" | \"infrastructure\"" + "\"infrastructure\" | \"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\"" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts", "deprecated": false, @@ -1077,7 +1077,7 @@ "label": "TechnicalRuleDataFieldName", "description": [], "signature": [ - "\"tags\" | \"kibana\" | \"@timestamp\" | \"event.action\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.instance.id\" | \"kibana.alert.start\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.severity\" | \"kibana.alert.status\" | \"kibana.version\" | \"ecs.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.workflow_status\" | \"kibana.alert.workflow_user\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.system_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"event.kind\" | \"event.module\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert\" | \"kibana.alert.rule\"" + "\"tags\" | \"kibana\" | \"@timestamp\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"event.action\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.instance.id\" | \"kibana.alert.start\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.severity\" | \"kibana.alert.status\" | \"kibana.version\" | \"ecs.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.workflow_status\" | \"kibana.alert.workflow_user\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.system_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"event.kind\" | \"event.module\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert\" | \"kibana.alert.rule\"" ], "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", "deprecated": false, @@ -1107,7 +1107,7 @@ "label": "ValidFeatureId", "description": [], "signature": [ - "\"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\" | \"infrastructure\"" + "\"infrastructure\" | \"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\"" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts", "deprecated": false, diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 61d440950cbf..4f65db7503fc 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 4ca4605d2eb9..5a9078575773 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 99858a653681..dc5fcdbd1d0f 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: 2022-09-28 +date: 2022-09-29 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.devdocs.json b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json new file mode 100644 index 000000000000..97b711804af1 --- /dev/null +++ b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json @@ -0,0 +1,1209 @@ +{ + "id": "@kbn/securitysolution-exception-list-components", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.EmptyViewerState", + "type": "Function", + "tags": [], + "label": "EmptyViewerState", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.EmptyViewerState.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCard", + "type": "Function", + "tags": [], + "label": "ExceptionItemCard", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionItemProps", + "text": "ExceptionItemProps" + }, + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCard.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardComments", + "type": "Function", + "tags": [], + "label": "ExceptionItemCardComments", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionItemCardCommentsProps", + "text": "ExceptionItemCardCommentsProps" + }, + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardComments.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardConditions", + "type": "Function", + "tags": [], + "label": "ExceptionItemCardConditions", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + "CriteriaConditionsProps", + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardConditions.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeader", + "type": "Function", + "tags": [], + "label": "ExceptionItemCardHeader", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionItemCardHeaderProps", + "text": "ExceptionItemCardHeaderProps" + }, + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeader.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfo", + "type": "Function", + "tags": [], + "label": "ExceptionItemCardMetaInfo", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionItemCardMetaInfoProps", + "text": "ExceptionItemCardMetaInfoProps" + }, + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfo.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItems", + "type": "Function", + "tags": [], + "label": "ExceptionItems", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItems.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.Pagination", + "type": "Function", + "tags": [], + "label": "Pagination", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.PaginationProps", + "text": "PaginationProps" + }, + ">" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.Pagination.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.SearchBar", + "type": "Function", + "tags": [], + "label": "SearchBar", + "description": [], + "signature": [ + "React.NamedExoticComponent" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.SearchBar.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ValueWithSpaceWarning", + "type": "Function", + "tags": [], + "label": "ValueWithSpaceWarning", + "description": [], + "signature": [ + "({ value, tooltipIconType, tooltipIconText, }: React.PropsWithChildren) => JSX.Element | null" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ValueWithSpaceWarning.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n value,\n tooltipIconType = 'iInCircle',\n tooltipIconText,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardCommentsProps", + "type": "Interface", + "tags": [], + "label": "ExceptionItemCardCommentsProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardCommentsProps.comments", + "type": "Array", + "tags": [], + "label": "comments", + "description": [], + "signature": [ + "EuiCommentProps", + "[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeaderProps", + "type": "Interface", + "tags": [], + "label": "ExceptionItemCardHeaderProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeaderProps.item", + "type": "Object", + "tags": [], + "label": "item", + "description": [], + "signature": [ + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeaderProps.actions", + "type": "Array", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ key: string; icon: string; label: string | boolean; onClick: () => void; }[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeaderProps.disableActions", + "type": "CompoundType", + "tags": [], + "label": "disableActions", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardHeaderProps.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps", + "type": "Interface", + "tags": [], + "label": "ExceptionItemCardMetaInfoProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps.item", + "type": "Object", + "tags": [], + "label": "item", + "description": [], + "signature": [ + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps.references", + "type": "Array", + "tags": [], + "label": "references", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.RuleReference", + "text": "RuleReference" + }, + "[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps.formattedDateComponent", + "type": "CompoundType", + "tags": [], + "label": "formattedDateComponent", + "description": [], + "signature": [ + "\"symbol\" | \"object\" | React.ComponentType | \"body\" | \"path\" | \"circle\" | \"filter\" | \"data\" | \"line\" | \"area\" | \"time\" | \"label\" | \"legend\" | \"image\" | \"link\" | \"menu\" | \"stop\" | \"base\" | \"text\" | \"title\" | \"s\" | \"small\" | \"svg\" | \"meta\" | \"script\" | \"summary\" | \"source\" | \"desc\" | \"q\" | \"pattern\" | \"mask\" | \"input\" | \"slot\" | \"style\" | \"head\" | \"section\" | \"big\" | \"sub\" | \"sup\" | \"animate\" | \"progress\" | \"view\" | \"output\" | \"var\" | \"map\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"form\" | \"main\" | \"abbr\" | \"address\" | \"article\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"canvas\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"p\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"select\" | \"span\" | \"strong\" | \"table\" | \"template\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemCardMetaInfoProps.securityLinkAnchorComponent", + "type": "CompoundType", + "tags": [], + "label": "securityLinkAnchorComponent", + "description": [], + "signature": [ + "\"symbol\" | \"object\" | React.ComponentType | \"body\" | \"path\" | \"circle\" | \"filter\" | \"data\" | \"line\" | \"area\" | \"time\" | \"label\" | \"legend\" | \"image\" | \"link\" | \"menu\" | \"stop\" | \"base\" | \"text\" | \"title\" | \"s\" | \"small\" | \"svg\" | \"meta\" | \"script\" | \"summary\" | \"source\" | \"desc\" | \"q\" | \"pattern\" | \"mask\" | \"input\" | \"slot\" | \"style\" | \"head\" | \"section\" | \"big\" | \"sub\" | \"sup\" | \"animate\" | \"progress\" | \"view\" | \"output\" | \"var\" | \"map\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"form\" | \"main\" | \"abbr\" | \"address\" | \"article\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"canvas\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"p\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"select\" | \"span\" | \"strong\" | \"table\" | \"template\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps", + "type": "Interface", + "tags": [], + "label": "ExceptionItemProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.disableActions", + "type": "CompoundType", + "tags": [], + "label": "disableActions", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.exceptionItem", + "type": "Object", + "tags": [], + "label": "exceptionItem", + "description": [], + "signature": [ + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.listType", + "type": "Enum", + "tags": [], + "label": "listType", + "description": [], + "signature": [ + "ExceptionListTypeEnum" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.ruleReferences", + "type": "Array", + "tags": [], + "label": "ruleReferences", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.editActionLabel", + "type": "string", + "tags": [], + "label": "editActionLabel", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.deleteActionLabel", + "type": "string", + "tags": [], + "label": "deleteActionLabel", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.securityLinkAnchorComponent", + "type": "CompoundType", + "tags": [], + "label": "securityLinkAnchorComponent", + "description": [], + "signature": [ + "\"symbol\" | \"object\" | React.ComponentType | \"body\" | \"path\" | \"circle\" | \"filter\" | \"data\" | \"line\" | \"area\" | \"time\" | \"label\" | \"legend\" | \"image\" | \"link\" | \"menu\" | \"stop\" | \"base\" | \"text\" | \"title\" | \"s\" | \"small\" | \"svg\" | \"meta\" | \"script\" | \"summary\" | \"source\" | \"desc\" | \"q\" | \"pattern\" | \"mask\" | \"input\" | \"slot\" | \"style\" | \"head\" | \"section\" | \"big\" | \"sub\" | \"sup\" | \"animate\" | \"progress\" | \"view\" | \"output\" | \"var\" | \"map\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"form\" | \"main\" | \"abbr\" | \"address\" | \"article\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"canvas\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"p\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"select\" | \"span\" | \"strong\" | \"table\" | \"template\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.formattedDateComponent", + "type": "CompoundType", + "tags": [], + "label": "formattedDateComponent", + "description": [], + "signature": [ + "\"symbol\" | \"object\" | React.ComponentType | \"body\" | \"path\" | \"circle\" | \"filter\" | \"data\" | \"line\" | \"area\" | \"time\" | \"label\" | \"legend\" | \"image\" | \"link\" | \"menu\" | \"stop\" | \"base\" | \"text\" | \"title\" | \"s\" | \"small\" | \"svg\" | \"meta\" | \"script\" | \"summary\" | \"source\" | \"desc\" | \"q\" | \"pattern\" | \"mask\" | \"input\" | \"slot\" | \"style\" | \"head\" | \"section\" | \"big\" | \"sub\" | \"sup\" | \"animate\" | \"progress\" | \"view\" | \"output\" | \"var\" | \"map\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"form\" | \"main\" | \"abbr\" | \"address\" | \"article\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"canvas\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"p\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"select\" | \"span\" | \"strong\" | \"table\" | \"template\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.getFormattedComments", + "type": "Function", + "tags": [], + "label": "getFormattedComments", + "description": [], + "signature": [ + "(comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]) => ", + "EuiCommentProps", + "[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.getFormattedComments.$1", + "type": "Array", + "tags": [], + "label": "comments", + "description": [], + "signature": [ + "({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.onDeleteException", + "type": "Function", + "tags": [], + "label": "onDeleteException", + "description": [], + "signature": [ + "(arg: ", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionListItemIdentifiers", + "text": "ExceptionListItemIdentifiers" + }, + ") => void" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.onDeleteException.$1", + "type": "Object", + "tags": [], + "label": "arg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.ExceptionListItemIdentifiers", + "text": "ExceptionListItemIdentifiers" + } + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.onEditException", + "type": "Function", + "tags": [], + "label": "onEditException", + "description": [], + "signature": [ + "(item: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => void" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionItemProps.onEditException.$1", + "type": "Object", + "tags": [], + "label": "item", + "description": [], + "signature": [ + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"keyword\" | \"ip\" | \"text\" | \"geo_point\" | \"geo_shape\" | \"date_nanos\" | \"long\" | \"double\" | \"date_range\" | \"ip_range\" | \"shape\" | \"short\" | \"binary\" | \"float\" | \"half_float\" | \"integer\" | \"byte\" | \"long_range\" | \"integer_range\" | \"float_range\" | \"double_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListItemIdentifiers", + "type": "Interface", + "tags": [], + "label": "ExceptionListItemIdentifiers", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListItemIdentifiers.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListItemIdentifiers.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListItemIdentifiers.namespaceType", + "type": "CompoundType", + "tags": [], + "label": "namespaceType", + "description": [], + "signature": [ + "\"single\" | \"agnostic\"" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListSummaryProps", + "type": "Interface", + "tags": [], + "label": "ExceptionListSummaryProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListSummaryProps.pagination", + "type": "Object", + "tags": [], + "label": "pagination", + "description": [], + "signature": [ + "Pagination" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ExceptionListSummaryProps.lastUpdated", + "type": "CompoundType", + "tags": [], + "label": "lastUpdated", + "description": [], + "signature": [ + "string | number | null" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.GetExceptionItemProps", + "type": "Interface", + "tags": [], + "label": "GetExceptionItemProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.GetExceptionItemProps.pagination", + "type": "Object", + "tags": [], + "label": "pagination", + "description": [], + "signature": [ + "Pagination", + " | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.GetExceptionItemProps.search", + "type": "string", + "tags": [], + "label": "search", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.GetExceptionItemProps.filters", + "type": "string", + "tags": [], + "label": "filters", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps", + "type": "Interface", + "tags": [], + "label": "PaginationProps", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps.ariaLabel", + "type": "string", + "tags": [], + "label": "ariaLabel", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps.pagination", + "type": "Object", + "tags": [], + "label": "pagination", + "description": [], + "signature": [ + "Pagination" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps.onPaginationChange", + "type": "Function", + "tags": [], + "label": "onPaginationChange", + "description": [], + "signature": [ + "(arg: ", + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.GetExceptionItemProps", + "text": "GetExceptionItemProps" + }, + ") => void" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.PaginationProps.onPaginationChange.$1", + "type": "Object", + "tags": [], + "label": "arg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-exception-list-components", + "scope": "common", + "docId": "kibKbnSecuritysolutionExceptionListComponentsPluginApi", + "section": "def-common.GetExceptionItemProps", + "text": "GetExceptionItemProps" + } + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReference", + "type": "Interface", + "tags": [], + "label": "RuleReference", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReference.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReference.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReference.ruleId", + "type": "string", + "tags": [], + "label": "ruleId", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReference.exceptionLists", + "type": "Array", + "tags": [], + "label": "exceptionLists", + "description": [], + "signature": [ + "{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReferences", + "type": "Interface", + "tags": [], + "label": "RuleReferences", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.RuleReferences.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: any[]", + "description": [], + "signature": [ + "[key: string]: any[]" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ListTypeText", + "type": "Enum", + "tags": [], + "label": "ListTypeText", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ViewerStatus", + "type": "Enum", + "tags": [], + "label": "ViewerStatus", + "description": [], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/securitysolution-exception-list-components", + "id": "def-common.ViewerFlyoutName", + "type": "Type", + "tags": [], + "label": "ViewerFlyoutName", + "description": [], + "signature": [ + "\"addException\" | \"editException\" | null" + ], + "path": "packages/kbn-securitysolution-exception-list-components/src/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx new file mode 100644 index 000000000000..1bcc5017dfc5 --- /dev/null +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -0,0 +1,39 @@ +--- +#### +#### 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: kibKbnSecuritysolutionExceptionListComponentsPluginApi +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: 2022-09-29 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] +--- +import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 76 | 0 | 67 | 1 | + +## Common + +### Functions + + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index c74046c4b1fa..a22707f2957b 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: 2022-09-28 +date: 2022-09-29 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 5f200ca33cd5..cdc1b3ea54ed 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: 2022-09-28 +date: 2022-09-29 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 75beb67b94d0..185f8a6c2a54 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: 2022-09-28 +date: 2022-09-29 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 2525d36a8393..ee826f79d080 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: 2022-09-28 +date: 2022-09-29 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 1729eb1e3192..79363d1648a7 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: 2022-09-28 +date: 2022-09-29 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 e7ea35711a93..bad03cd4ead9 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: 2022-09-28 +date: 2022-09-29 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 061d996305ac..d2e797f19a3e 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: 2022-09-28 +date: 2022-09-29 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 19a2ed45a48b..aadd62030a1b 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: 2022-09-28 +date: 2022-09-29 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 247718cc1d2a..37f5836577b4 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: 2022-09-28 +date: 2022-09-29 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 53f33ec3d564..7aa2954ee0c0 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: 2022-09-28 +date: 2022-09-29 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 0be9d3e0886f..99fe2b585b0b 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: 2022-09-28 +date: 2022-09-29 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 b3d353cc6987..e132797fea85 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: 2022-09-28 +date: 2022-09-29 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 aefc65e7066a..48d0d4756ef6 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: 2022-09-28 +date: 2022-09-29 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 25cf89691a72..5c184c9a583c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index aaf7c6592481..249f6a00cf7e 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: 2022-09-28 +date: 2022-09-29 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index b12d28f86530..e5e0f236974f 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index 4fafd2467103..b78abda31ac5 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 401b8763ce31..dda07aa08205 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: 2022-09-28 +date: 2022-09-29 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 25b21295a4eb..92fa45970a95 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: 2022-09-28 +date: 2022-09-29 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 5867910e33d2..67fd1537b953 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: 2022-09-28 +date: 2022-09-29 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 757324760d8e..dba90c297e1b 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: 2022-09-28 +date: 2022-09-29 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 19d3fcf3f0ac..eb63fe71719d 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: 2022-09-28 +date: 2022-09-29 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 f75c2846a047..312cc6dc442d 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: 2022-09-28 +date: 2022-09-29 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 89d4d761d558..fc5ddb138a55 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: 2022-09-28 +date: 2022-09-29 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 2056f2314bce..00f4e0b05251 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: 2022-09-28 +date: 2022-09-29 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 495d91eac254..22e5de7cb344 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: 2022-09-28 +date: 2022-09-29 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 7767a12066d2..de0d8b5ff101 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: 2022-09-28 +date: 2022-09-29 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 e0d65073b737..b1597719e26a 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: 2022-09-28 +date: 2022-09-29 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 8c798dd34a9a..254ce88f6ed0 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: 2022-09-28 +date: 2022-09-29 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 243de44838e1..4b7ce948af06 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: 2022-09-28 +date: 2022-09-29 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 38664c4b62fd..4bebd9fa27f9 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: 2022-09-28 +date: 2022-09-29 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 3f237d268f07..a3b2ddd97a17 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: 2022-09-28 +date: 2022-09-29 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 3ff43ae1b626..0a1d772cc031 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: 2022-09-28 +date: 2022-09-29 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 48e497a99004..70d3cf84c5ef 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: 2022-09-28 +date: 2022-09-29 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 15f626088ad2..61fe760de503 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: 2022-09-28 +date: 2022-09-29 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 2e0d73cc4362..02064c4eacc6 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: 2022-09-28 +date: 2022-09-29 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 e9accf72f5a0..86606778f8df 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: 2022-09-28 +date: 2022-09-29 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 6309733dc1ba..180ae33f7885 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: 2022-09-28 +date: 2022-09-29 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index c770dd867195..7c2f7cc09c77 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 4b531126964e..ef8d669ed27a 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: 2022-09-28 +date: 2022-09-29 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 8073cadd73d8..0e8f9505b35f 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index c05c4d734c49..c2a9a2019695 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: 2022-09-28 +date: 2022-09-29 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 660e36ef4374..9662e0b7958f 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: 2022-09-28 +date: 2022-09-29 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 93828baf0544..f279425a1128 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index fc2ed806bd5a..70dd586f3b9f 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: 2022-09-28 +date: 2022-09-29 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 fe8a35b28280..2d4b1f7a3e30 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index b072b928a5fe..f0c472ec736d 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: 2022-09-28 +date: 2022-09-29 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 2cc7dbb077e4..421399623af6 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 4c380a6aada5..8c06707332c2 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 4658f66ec9f0..a03912522bea 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 5c785be7541a..7d6e18575e25 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 058bbf15a853..3d387dbca9d5 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: 2022-09-28 +date: 2022-09-29 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index ffad848d71c2..80555e578ee9 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index b7a7e0d49898..0396c24bc40e 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: 2022-09-28 +date: 2022-09-29 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 9c4fac9fc222..de93a8fe050a 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: 2022-09-28 +date: 2022-09-29 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 a08a1f8c35b2..fe8d933d19a3 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: 2022-09-28 +date: 2022-09-29 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 484f22d4d3fa..e9e1e8d327df 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 86360f220dc2..96ce8b5f78b1 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index db2dd293c3e4..2a28c3743374 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: 2022-09-28 +date: 2022-09-29 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 a8b6bbd60563..7ab80f87e401 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: 2022-09-28 +date: 2022-09-29 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 e8b72f7cee2d..21d349683d8e 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: 2022-09-28 +date: 2022-09-29 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 fc9a2e294a61..b7d40f28432a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 75d2cd54a2d8..0cb5e40b3b4c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.devdocs.json b/api_docs/license_api_guard.devdocs.json index a554c2c4b789..03acec45780d 100644 --- a/api_docs/license_api_guard.devdocs.json +++ b/api_docs/license_api_guard.devdocs.json @@ -94,13 +94,7 @@ "description": [], "signature": [ "(handler: ", { "pluginId": "core", diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 42f4d0805fc3..63d15a7c4065 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: 2022-09-28 +date: 2022-09-29 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 ca3e9e1a23c1..88e17bcee6fe 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: 2022-09-28 +date: 2022-09-29 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 67cd87e28a5c..c7000d51761c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index c0fba6cbabbb..9deb279fb73b 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 7a332ae0f68b..2cec82774807 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: 2022-09-28 +date: 2022-09-29 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 c821d5118393..2667e8f19bae 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: 2022-09-28 +date: 2022-09-29 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 30e6fc5864b6..558352f89505 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 83e81edff74a..b1abe52cefe1 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 8b27dbc3d784..b9332eb4163e 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: 2022-09-28 +date: 2022-09-29 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 d0c0e4b93012..23c398899ccb 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index c8d4e9b1fbf7..bfa907ab0926 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 79f080105b7b..4336085d004c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index d4d3483ef247..fd03a2f285ab 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -7613,13 +7613,7 @@ "label": "context", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { licensing: Promise<", { "pluginId": "licensing", @@ -7637,13 +7631,7 @@ "text": "AlertingApiRequestHandlerContext" }, ">; core: Promise<", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreRequestHandlerContext", - "text": "CoreRequestHandlerContext" - }, + "CoreRequestHandlerContext", ">; }" ], "path": "x-pack/plugins/observability/server/routes/types.ts", @@ -7944,7 +7932,7 @@ "label": "ObservabilityConfig", "description": [], "signature": [ - "{ readonly unsafe: Readonly<{} & { slo: Readonly<{} & { enabled: boolean; }>; alertDetails: Readonly<{} & { enabled: boolean; }>; }>; readonly annotations: Readonly<{} & { index: string; enabled: boolean; }>; }" + "{ readonly unsafe: Readonly<{} & { slo: Readonly<{} & { enabled: boolean; }>; alertDetails: Readonly<{} & { enabled: boolean; }>; }>; readonly annotations: Readonly<{} & { enabled: boolean; index: string; }>; }" ], "path": "x-pack/plugins/observability/server/index.ts", "deprecated": false, @@ -9798,13 +9786,7 @@ "description": [], "signature": [ "{ getScopedAnnotationsClient: (requestContext: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { licensing: Promise<", { "pluginId": "licensing", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 4c2ca8b17a13..db3f0906e614 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 942900853abd..c569e301708d 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index bcddc85faf5c..568f38705df0 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: 2022-09-28 +date: 2022-09-29 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 | |--------------|----------|------------------------| -| 474 | 395 | 38 | +| 477 | 397 | 38 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 31860 | 179 | 21432 | 1007 | +| 31953 | 179 | 21502 | 1003 | ## Plugin Directory @@ -30,7 +30,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 214 | 0 | 209 | 19 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 1 | 32 | 2 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 9 | 0 | 0 | 2 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 370 | 0 | 361 | 22 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 379 | 0 | 370 | 24 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 38 | 0 | 38 | 52 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 80 | 1 | 71 | 2 | @@ -42,10 +42,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 18 | 0 | 2 | 3 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 212 | 0 | 204 | 7 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2684 | 0 | 35 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2688 | 0 | 30 | 0 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 103 | 0 | 84 | 1 | -| | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 144 | 0 | 139 | 10 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 104 | 0 | 85 | 1 | +| | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 120 | 0 | 113 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 52 | 0 | 51 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3213 | 33 | 2509 | 23 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | @@ -152,7 +152,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 452 | 1 | 346 | 33 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 513 | 1 | 486 | 48 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 512 | 1 | 485 | 48 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 132 | 0 | 91 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 206 | 0 | 142 | 9 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 61 | 0 | 59 | 2 | @@ -175,7 +175,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 53 | 0 | 50 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 679 | 12 | 649 | 18 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 693 | 12 | 663 | 18 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -261,6 +261,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 16 | 0 | 16 | 0 | | | Kibana Core | - | 4 | 0 | 0 | 0 | | | Kibana Core | - | 10 | 1 | 10 | 0 | +| | Kibana Core | - | 14 | 0 | 11 | 0 | | | Kibana Core | - | 25 | 5 | 25 | 1 | | | Kibana Core | - | 7 | 0 | 7 | 1 | | | Kibana Core | - | 392 | 1 | 154 | 0 | @@ -388,6 +389,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 74 | 0 | 71 | 0 | | | [Owner missing] | Security Solution auto complete | 56 | 1 | 41 | 1 | | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | +| | [Owner missing] | - | 76 | 0 | 67 | 1 | | | [Owner missing] | Security Solution utilities for React hooks | 15 | 0 | 7 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 145 | 0 | 127 | 0 | | | [Owner missing] | io ts utilities and types to be shared with plugins from the security solution project | 505 | 1 | 492 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 191e4e563cb3..099351a2d0f2 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: 2022-09-28 +date: 2022-09-29 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 fb048ac82442..2ef17ce2192f 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index c0b505ed6394..8a248f78f1fa 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: 2022-09-28 +date: 2022-09-29 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 4088e39f9710..9589edc812df 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: 2022-09-28 +date: 2022-09-29 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 4f8cf9c8e8db..1964dc994cd2 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 424057fd0db7..089f973bc0bf 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -1917,7 +1917,7 @@ "\nID of the Kibana feature associated with the index.\nUsed by alerts-as-data RBAC.\n\nNote from @dhurley14\nThe purpose of the `feature` param is to force the user to update\nthe data structure which contains the mapping of consumers to alerts\nas data indices. The idea is it is typed such that it forces the\nuser to go to the code and modify it. At least until a better system\nis put in place or we move the alerts as data client out of rule registry.\n" ], "signature": [ - "\"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\" | \"infrastructure\"" + "\"infrastructure\" | \"observability\" | \"logs\" | \"apm\" | \"uptime\" | \"siem\"" ], "path": "x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts", "deprecated": false, @@ -4270,7 +4270,7 @@ "label": "ParsedTechnicalFields", "description": [], "signature": [ - "{ readonly '@timestamp': string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.space_ids\": string[]; readonly \"kibana.alert.uuid\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.rule.name\": string; readonly tags?: string[] | undefined; readonly 'event.action'?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly 'event.kind'?: string | undefined; }" + "{ readonly '@timestamp': string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.space_ids\": string[]; readonly \"kibana.alert.uuid\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.rule.name\": string; readonly tags?: string[] | undefined; readonly 'event.action'?: string | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly 'event.kind'?: string | undefined; }" ], "path": "x-pack/plugins/rule_registry/common/parse_technical_fields.ts", "deprecated": false, diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 15c672f3f6fe..d4539ef64efa 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: 2022-09-28 +date: 2022-09-29 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 001d245e9adf..8112f8684b55 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.devdocs.json b/api_docs/saved_objects.devdocs.json index 6d40e242e3cd..71d53634b754 100644 --- a/api_docs/saved_objects.devdocs.json +++ b/api_docs/saved_objects.devdocs.json @@ -1557,18 +1557,6 @@ "plugin": "savedObjectsTaggingOss", "path": "src/plugins/saved_objects_tagging_oss/public/api.ts" }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/services/saved_object_loader.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/services/saved_object_loader.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/services/saved_object_loader.ts" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/application/actions/clone_panel_action.tsx" @@ -1580,22 +1568,6 @@ { "plugin": "dashboard", "path": "src/plugins/dashboard/public/application/actions/clone_panel_action.tsx" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/lib/dashboard_tagging.ts" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/lib/dashboard_tagging.ts" } ], "children": [ @@ -3121,12 +3093,7 @@ "deprecated": true, "removeBy": "8.8.0", "trackAdoption": false, - "references": [ - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" - } - ] + "references": [] }, { "parentPluginId": "savedObjects", diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 51bc9b36316c..613f942ef906 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: 2022-09-28 +date: 2022-09-29 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 3a6b9b0a9e73..23cf7d31ca48 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: 2022-09-28 +date: 2022-09-29 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 8fb7925eb780..c9cc4aa61ea7 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: 2022-09-28 +date: 2022-09-29 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 9a0469a70f64..1450399aefd8 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: 2022-09-28 +date: 2022-09-29 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 8abc78f1fdc7..1ed5aa9d511e 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.devdocs.json b/api_docs/saved_search.devdocs.json index df52fe959fea..8e6b2e5503d3 100644 --- a/api_docs/saved_search.devdocs.json +++ b/api_docs/saved_search.devdocs.json @@ -661,7 +661,7 @@ "label": "sharingSavedObjectProps", "description": [], "signature": [ - "{ outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" + "{ outcome?: \"conflict\" | \"exactMatch\" | \"aliasMatch\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" ], "path": "src/plugins/saved_search/public/services/saved_searches/types.ts", "deprecated": false, diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index a39c3adbc820..6c1900781f66 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.devdocs.json b/api_docs/screenshot_mode.devdocs.json index c6be83e92847..ab834dfc733b 100644 --- a/api_docs/screenshot_mode.devdocs.json +++ b/api_docs/screenshot_mode.devdocs.json @@ -139,13 +139,7 @@ "label": "ScreenshotModeRequestHandlerContext", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - }, + "RequestHandlerContext", " & { screenshotMode: Promise<{ isScreenshot: boolean; }>; }" ], "path": "src/plugins/screenshot_mode/server/types.ts", diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 6eaf962f670f..883f8e26e2cc 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: 2022-09-28 +date: 2022-09-29 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 8cb0692d0c85..df0feab2c445 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index e6f290046222..814d0173f69c 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index 8ad8f2f5fe21..c3ab461b2eb2 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -768,13 +768,7 @@ "label": "core", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.CoreRequestHandlerContext", - "text": "CoreRequestHandlerContext" - } + "CoreRequestHandlerContext" ], "path": "x-pack/plugins/security_solution/server/types.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index c59c106fce25..a86c48a48dfc 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index ad4253bd0d7e..66978ad664e0 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: 2022-09-28 +date: 2022-09-29 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 e6f9de45dbb5..0086ad02ab5a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 8a45ec4089ad..7bce31d6e183 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: 2022-09-28 +date: 2022-09-29 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 94f882172834..decf22f6da09 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: 2022-09-28 +date: 2022-09-29 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 cb3df93effb0..e68ecfcdfb99 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: 2022-09-28 +date: 2022-09-29 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 a71b61831fb6..5363b913fbe0 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: 2022-09-28 +date: 2022-09-29 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 b84502a5d8c5..7377ef34d529 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: 2022-09-28 +date: 2022-09-29 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 dfdbd20746f5..1a95f8384f24 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: 2022-09-28 +date: 2022-09-29 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 edb998b57d6f..355a38255c9c 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: 2022-09-28 +date: 2022-09-29 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 c23dd99eb95e..e607fc3598bd 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: 2022-09-28 +date: 2022-09-29 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 5e8ae624dfe8..f538309dd462 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: 2022-09-28 +date: 2022-09-29 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 80ac38a8ed99..bcebf8f8b934 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index 5875aa47c130..4b5ec5ce2897 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -3192,7 +3192,7 @@ "IFieldSubType", " | undefined; type?: string | undefined; })[]; id: string; title: string; filters?: ", "Filter", - "[] | undefined; dataViewId: string | null; sort: ", + "[] | undefined; dataViewId: string | null; savedObjectId: string | null; sort: ", "SortColumnTimeline", "[]; version: string | null; filterManager?: ", { @@ -3218,7 +3218,7 @@ }, "; description?: string | null | undefined; esTypes?: string[] | undefined; example?: string | number | null | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", "IFieldSubType", - " | undefined; type?: string | undefined; })[]; savedObjectId: string | null; isLoading: boolean; dataProviders: ", + " | undefined; type?: string | undefined; })[]; isLoading: boolean; dataProviders: ", { "pluginId": "timelines", "scope": "common", diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index ecb0a115ea51..7a96d016a99c 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: 2022-09-28 +date: 2022-09-29 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 70b733e2c869..7d3c611c084b 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 316d5941b9d2..d6351e414ae4 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -3139,7 +3139,7 @@ "description": [], "signature": [ "BasicFields", - " & { tags?: string[] | undefined; kibana?: string[] | undefined; \"@timestamp\"?: string[] | undefined; \"event.action\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; } & { [x: string]: unknown[]; }" + " & { tags?: string[] | undefined; kibana?: string[] | undefined; \"@timestamp\"?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"event.action\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; } & { [x: string]: unknown[]; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -3585,20 +3585,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "triggersActionsUi", - "id": "def-public.AlertStatus.actionSubgroup", - "type": "string", - "tags": [], - "label": "actionSubgroup", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/alerting/common/alert_summary.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "triggersActionsUi", "id": "def-public.AlertStatus.activeStartDate", diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index cfa8f8965332..e40b4b0c4d44 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 513 | 1 | 486 | 48 | +| 512 | 1 | 485 | 48 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 25fd9d32aac5..9a4994646988 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: 2022-09-28 +date: 2022-09-29 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 f85563ce18a7..e1fd951d5e2a 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 2aae0fbddf27..254b1228a31f 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 261ef25b80e9..5015d2debdc4 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: 2022-09-28 +date: 2022-09-29 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 1e75d7da1deb..5a40018e6081 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index d2cdaf4d89c8..f57d70892e70 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: 2022-09-28 +date: 2022-09-29 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 5f6baf6d86c5..f40f15636c94 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: 2022-09-28 +date: 2022-09-29 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 470423b8266a..d462e4d19616 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: 2022-09-28 +date: 2022-09-29 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 ff5773111414..0b2993331260 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: 2022-09-28 +date: 2022-09-29 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 1da13d1fcc09..cc20cda9db10 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: 2022-09-28 +date: 2022-09-29 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 d30f1b6a560a..609c06b1a92c 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: 2022-09-28 +date: 2022-09-29 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 f8d894bdaa7d..3f8c15b314a0 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: 2022-09-28 +date: 2022-09-29 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 deb819de77df..5aa2bd8b01f5 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: 2022-09-28 +date: 2022-09-29 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 042fb74b10af..2219fc96f2e3 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: 2022-09-28 +date: 2022-09-29 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 2458fbb4d9de..2608ea12c24f 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: 2022-09-28 +date: 2022-09-29 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 606b4dcf05e1..e535d8ca970e 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: 2022-09-28 +date: 2022-09-29 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 e56cd5d8f509..c6bd5fba0b06 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: 2022-09-28 +date: 2022-09-29 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 9ac63dfe30cc..8caa2a67dab8 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: 2022-09-28 +date: 2022-09-29 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 adc8f3022cb1..95b3390b94ce 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -3042,7 +3042,7 @@ "label": "sharingSavedObjectProps", "description": [], "signature": [ - "{ outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" + "{ outcome?: \"conflict\" | \"exactMatch\" | \"aliasMatch\" | undefined; aliasTargetId?: string | undefined; aliasPurpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; errorJSON?: string | undefined; } | undefined" ], "path": "src/plugins/visualizations/public/types.ts", "deprecated": false, @@ -9284,6 +9284,203 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration", + "type": "Interface", + "tags": [], + "label": "MetricVisConfiguration", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.layerId", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.layerType", + "type": "string", + "tags": [], + "label": "layerType", + "description": [], + "signature": [ + "\"data\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.metricAccessor", + "type": "string", + "tags": [], + "label": "metricAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.secondaryMetricAccessor", + "type": "string", + "tags": [], + "label": "secondaryMetricAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.maxAccessor", + "type": "string", + "tags": [], + "label": "maxAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.breakdownByAccessor", + "type": "string", + "tags": [], + "label": "breakdownByAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.collapseFn", + "type": "string", + "tags": [], + "label": "collapseFn", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.subtitle", + "type": "string", + "tags": [], + "label": "subtitle", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.secondaryPrefix", + "type": "string", + "tags": [], + "label": "secondaryPrefix", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.progressDirection", + "type": "CompoundType", + "tags": [], + "label": "progressDirection", + "description": [], + "signature": [ + "LayoutDirection", + " | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.palette", + "type": "Object", + "tags": [], + "label": "palette", + "description": [], + "signature": [ + "PaletteOutput", + "<", + "CustomPaletteParams", + "> | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.MetricVisConfiguration.maxCols", + "type": "number", + "tags": [], + "label": "maxCols", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.MovingAverageParams", @@ -11891,6 +12088,14 @@ "docId": "kibVisualizationsPluginApi", "section": "def-common.TableVisConfiguration", "text": "TableVisConfiguration" + }, + " | ", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.MetricVisConfiguration", + "text": "MetricVisConfiguration" } ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index e5907aaf8a8a..6e29fc3d03f2 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: 2022-09-28 +date: 2022-09-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 679 | 12 | 649 | 18 | +| 693 | 12 | 663 | 18 | ## Client From ea046acd24a6ec30789c90b6a5c5c1b1148f65ee Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 29 Sep 2022 09:32:55 +0300 Subject: [PATCH 095/185] [Agg based pie][Lens]Navigate to lens Agg based pie (#140879) * Added support for converting Agg-based pie to Lens. * Fixed problem with removed index pattern. * Made some changes for uniformity. * Fixed label of go back to. * Fixed legend display if uiState was changed. * Fixed configuration. * Removed as string[]. * Fixed code according to the nit. * Allowed 3 segments and restricted converting of split_row/split_column. * Fixed tests. * Refactored functional tests. * Added functional tests for pie. Co-authored-by: Yaroslav Kuznietsov Co-authored-by: Joe Reuter --- .../pie_vis_function.test.ts.snap | 2 + .../expression_functions/pie_vis_function.ts | 1 + .../common/types/expression_renderers.ts | 1 + .../partition_vis_renderer.tsx | 13 +- src/plugins/vis_types/pie/kibana.json | 4 +- .../configurations/index.test.ts | 104 ++++++++++ .../convert_to_lens/configurations/index.ts | 79 +++++++ .../pie/public/convert_to_lens/index.test.ts | 77 +++++++ .../pie/public/convert_to_lens/index.ts | 79 +++++++ .../pie/public/convert_to_lens/types.ts | 20 ++ src/plugins/vis_types/pie/public/plugin.ts | 13 +- .../pie/public/sample_vis.test.mocks.ts | 3 + src/plugins/vis_types/pie/public/services.ts | 13 ++ .../vis_types/pie/public/vis_type/pie.ts | 7 + .../common/convert_to_lens/constants.ts | 64 ++++++ .../convert_to_lens/types/configurations.ts | 80 +++++--- .../public/convert_to_lens/schemas.ts | 26 ++- .../components/visualize_top_nav.tsx | 10 +- .../workspace_panel/chart_switch.tsx | 6 +- .../indexpattern_suggestions.test.tsx | 6 +- .../indexpattern_suggestions.ts | 1 + .../operations/layer_helpers.ts | 30 ++- .../visualize_agg_based_vis_actions.ts | 2 +- x-pack/plugins/lens/public/types.ts | 8 +- .../datatable/visualization.tsx | 10 +- .../partition/suggestions.test.ts | 24 ++- .../visualizations/partition/suggestions.ts | 13 +- .../partition/visualization.tsx | 43 ++++ .../visualizations/xy/xy_suggestions.ts | 7 +- .../test/functional/apps/lens/group3/index.ts | 2 +- .../group3/open_in_lens/agg_based/index.ts | 14 ++ .../lens/group3/open_in_lens/agg_based/pie.ts | 68 +++++++ .../apps/lens/group3/open_in_lens/index.ts | 15 ++ .../group3/open_in_lens/tsvb/dashboard.ts | 101 +++++++++ .../lens/group3/open_in_lens/tsvb/index.ts | 16 ++ .../lens/group3/open_in_lens/tsvb/metric.ts | 44 ++++ .../group3/open_in_lens/tsvb/timeseries.ts | 87 ++++++++ .../apps/lens/group3/tsvb_open_in_lens.ts | 192 ------------------ 38 files changed, 1010 insertions(+), 275 deletions(-) create mode 100644 src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts create mode 100644 src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts create mode 100644 src/plugins/vis_types/pie/public/convert_to_lens/index.test.ts create mode 100644 src/plugins/vis_types/pie/public/convert_to_lens/index.ts create mode 100644 src/plugins/vis_types/pie/public/convert_to_lens/types.ts create mode 100644 src/plugins/vis_types/pie/public/services.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/pie.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/index.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/dashboard.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/index.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/metric.ts create mode 100644 x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/timeseries.ts delete mode 100644 x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 0f64f4c0a477..3fd9966e7524 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -26,6 +26,7 @@ Object { "as": "partitionVis", "type": "render", "value": Object { + "canNavigateToLens": false, "params": Object { "listenOnChange": true, }, @@ -160,6 +161,7 @@ Object { "as": "partitionVis", "type": "render", "value": Object { + "canNavigateToLens": false, "params": Object { "listenOnChange": true, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 5b69fbc6194f..119d45f579eb 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -188,6 +188,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: args.isDonut ? ChartTypes.DONUT : ChartTypes.PIE, + canNavigateToLens: Boolean(handlers?.variables?.canNavigateToLens), params: { listenOnChange: true, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 2f436de90e13..6a8fd2935ba5 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -108,6 +108,7 @@ export interface RenderValue { visType: ChartTypes; visConfig: PartitionVisParams; syncColors: boolean; + canNavigateToLens?: boolean; } export enum LabelPositions { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx index 546b70d98c6e..4b6fe45e4df9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx @@ -49,7 +49,11 @@ export const getPartitionVisRenderer: ( displayName: strings.getDisplayName(), help: strings.getHelpDescription(), reuseDomNode: true, - render: async (domNode, { visConfig, visData, visType, syncColors }, handlers) => { + render: async ( + domNode, + { visConfig, visData, visType, syncColors, canNavigateToLens }, + handlers + ) => { const { core, plugins } = getStartDeps(); handlers.onDestroy(() => { @@ -62,9 +66,12 @@ export const getPartitionVisRenderer: ( const visualizationType = extractVisualizationType(executionContext); if (containerType && visualizationType) { - plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [ + const events = [ `render_${visualizationType}_${visType}`, - ]); + canNavigateToLens ? `render_${visualizationType}_${visType}_convertable` : undefined, + ].filter((event): event is string => Boolean(event)); + + plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, events); } handlers.done(); }; diff --git a/src/plugins/vis_types/pie/kibana.json b/src/plugins/vis_types/pie/kibana.json index abed576cc673..4c5ee6b50579 100644 --- a/src/plugins/vis_types/pie/kibana.json +++ b/src/plugins/vis_types/pie/kibana.json @@ -3,8 +3,8 @@ "version": "kibana", "ui": true, "server": true, - "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection", "expressionPartitionVis"], - "requiredBundles": ["visDefaultEditor"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection", "expressionPartitionVis", "dataViews"], + "requiredBundles": ["visDefaultEditor", "kibanaUtils"], "extraPublicDirs": ["common/index"], "owner": { "name": "Vis Editors", diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts new file mode 100644 index 000000000000..0a10a5bd7c0c --- /dev/null +++ b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getConfiguration } from '.'; +import { samplePieVis } from '../../sample_vis.test.mocks'; + +describe('getConfiguration', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('should return correct configuration', () => { + samplePieVis.uiState.get.mockReturnValueOnce(undefined); + expect( + getConfiguration('test1', samplePieVis as any, { + metrics: ['metric-1'], + buckets: ['bucket-1'], + }) + ).toEqual({ + layers: [ + { + categoryDisplay: undefined, + emptySizeRatio: undefined, + layerId: 'test1', + layerType: 'data', + legendDisplay: 'show', + legendMaxLines: 1, + legendPosition: 'right', + legendSize: 'large', + metric: 'metric-1', + nestedLegend: true, + numberDisplay: 'percent', + percentDecimals: 2, + primaryGroups: ['bucket-1'], + secondaryGroups: [], + showValuesInLegend: true, + truncateLegend: true, + }, + ], + shape: 'donut', + palette: undefined, + }); + }); + + test('should return legendDisplay = show if uiState contains truthy value', () => { + samplePieVis.uiState.get.mockReturnValueOnce(true); + expect( + getConfiguration( + 'test1', + { ...samplePieVis, params: { ...samplePieVis.params, legendDisplay: 'hide' } } as any, + { + metrics: ['metric-1'], + buckets: ['bucket-1'], + } + ) + ).toEqual({ + layers: [expect.objectContaining({ legendDisplay: 'show' })], + shape: 'donut', + palette: undefined, + }); + }); + + test('should return legendDisplay = hide if uiState contains falsy value', () => { + samplePieVis.uiState.get.mockReturnValueOnce(false); + expect( + getConfiguration( + 'test1', + { ...samplePieVis, params: { ...samplePieVis.params, legendDisplay: 'show' } } as any, + { + metrics: ['metric-1'], + buckets: ['bucket-1'], + } + ) + ).toEqual({ + layers: [expect.objectContaining({ legendDisplay: 'hide' })], + shape: 'donut', + palette: undefined, + }); + }); + + test('should return value of legendDisplay if uiState contains undefined value', () => { + samplePieVis.uiState.get.mockReturnValueOnce(undefined); + const legendDisplay = 'show'; + expect( + getConfiguration( + 'test1', + { ...samplePieVis, params: { ...samplePieVis.params, legendDisplay } } as any, + { + metrics: ['metric-1'], + buckets: ['bucket-1'], + } + ) + ).toEqual({ + layers: [expect.objectContaining({ legendDisplay })], + shape: 'donut', + palette: undefined, + }); + }); +}); diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts new file mode 100644 index 000000000000..9a3420581c1f --- /dev/null +++ b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LegendDisplay, PartitionVisParams } from '@kbn/expression-partition-vis-plugin/common'; +import { + CategoryDisplayTypes, + NumberDisplayTypes, + PartitionVisConfiguration, +} from '@kbn/visualizations-plugin/common/convert_to_lens'; +import { Vis } from '@kbn/visualizations-plugin/public'; + +const getLayers = ( + layerId: string, + vis: Vis, + metrics: string[], + buckets: string[] +): PartitionVisConfiguration['layers'] => { + const legendOpen = vis.uiState.get('vis.legendOpen'); + const legendDisplayFromUiState = + legendOpen !== undefined ? (legendOpen ? LegendDisplay.SHOW : LegendDisplay.HIDE) : undefined; + + const showValuesInLegend = + vis.params.labels.values ?? + vis.params.showValuesInLegend ?? + vis.type.visConfig.defaults.showValuesInLegend; + + return [ + { + layerId, + layerType: 'data' as const, + primaryGroups: buckets, + secondaryGroups: [], + metric: metrics[0], + numberDisplay: + showValuesInLegend === false + ? NumberDisplayTypes.HIDDEN + : vis.params.labels.valuesFormat ?? vis.type.visConfig.defaults.labels.valuesFormat, + categoryDisplay: vis.params.labels.show + ? vis.params.labels.position ?? vis.type.visConfig.defaults.labels.position + : CategoryDisplayTypes.HIDE, + legendDisplay: + legendDisplayFromUiState ?? + vis.params.legendDisplay ?? + vis.type.visConfig.defaults.legendDisplay, + legendPosition: vis.params.legendPosition ?? vis.type.visConfig.defaults.legendPosition, + showValuesInLegend, + nestedLegend: vis.params.nestedLegend ?? vis.type.visConfig.defaults.nestedLegend, + percentDecimals: + vis.params.labels.percentDecimals ?? vis.type.visConfig.defaults.labels.percentDecimals, + emptySizeRatio: vis.params.emptySizeRatio ?? vis.type.visConfig.defaults.emptySizeRatio, + legendMaxLines: vis.params.maxLegendLines ?? vis.type.visConfig.defaults.maxLegendLines, + legendSize: vis.params.legendSize ?? vis.type.visConfig.defaults.legendSize, + truncateLegend: vis.params.truncateLegend ?? vis.type.visConfig.defaults.truncateLegend, + }, + ]; +}; + +export const getConfiguration = ( + layerId: string, + vis: Vis, + { + metrics, + buckets, + }: { + metrics: string[]; + buckets: string[]; + } +): PartitionVisConfiguration => { + return { + shape: vis.params.isDonut ? 'donut' : 'pie', + layers: getLayers(layerId, vis, metrics, buckets), + palette: vis.params.palette, + }; +}; diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/index.test.ts b/src/plugins/vis_types/pie/public/convert_to_lens/index.test.ts new file mode 100644 index 000000000000..c1e39d741f84 --- /dev/null +++ b/src/plugins/vis_types/pie/public/convert_to_lens/index.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { convertToLens } from '.'; +import { samplePieVis } from '../sample_vis.test.mocks'; + +const mockGetColumnsFromVis = jest.fn(); +const mockGetConfiguration = jest.fn().mockReturnValue({}); + +jest.mock('../services', () => ({ + getDataViewsStart: jest.fn(() => ({ get: () => ({}), getDefault: () => ({}) })), +})); + +jest.mock('@kbn/visualizations-plugin/public', () => ({ + convertToLensModule: Promise.resolve({ + getColumnsFromVis: jest.fn(() => mockGetColumnsFromVis()), + }), + getDataViewByIndexPatternId: jest.fn(() => ({ id: 'index-pattern' })), +})); + +jest.mock('./configurations', () => ({ + getConfiguration: jest.fn(() => mockGetConfiguration()), +})); + +describe('convertToLens', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should return null if getColumnsFromVis returns null', async () => { + mockGetColumnsFromVis.mockReturnValue(null); + const result = await convertToLens(samplePieVis as any, {} as any); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + + test('should return null if more than three split slice levels', async () => { + mockGetColumnsFromVis.mockReturnValue({ + buckets: ['1', '2', '3', '4'], + }); + const result = await convertToLens(samplePieVis as any, {} as any); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + + test('should return null if no one split slices', async () => { + mockGetColumnsFromVis.mockReturnValue({ + buckets: [], + }); + const result = await convertToLens(samplePieVis as any, {} as any); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(result).toBeNull(); + }); + + test('should state for valid vis', async () => { + mockGetColumnsFromVis.mockReturnValue({ + buckets: ['2'], + columns: [{ columnId: '2' }, { columnId: '1' }], + }); + const result = await convertToLens(samplePieVis as any, {} as any); + expect(mockGetColumnsFromVis).toBeCalledTimes(1); + expect(mockGetConfiguration).toBeCalledTimes(1); + expect(result?.type).toEqual('lnsPie'); + expect(result?.layers.length).toEqual(1); + expect(result?.layers[0]).toEqual( + expect.objectContaining({ + columnOrder: [], + columns: [{ columnId: '2' }, { columnId: '1' }], + }) + ); + }); +}); diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/index.ts b/src/plugins/vis_types/pie/public/convert_to_lens/index.ts new file mode 100644 index 000000000000..5b1973507c7d --- /dev/null +++ b/src/plugins/vis_types/pie/public/convert_to_lens/index.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Column, ColumnWithMeta } from '@kbn/visualizations-plugin/common'; +import { + convertToLensModule, + getDataViewByIndexPatternId, +} from '@kbn/visualizations-plugin/public'; +import uuid from 'uuid'; +import { getDataViewsStart } from '../services'; +import { getConfiguration } from './configurations'; +import { ConvertPieToLensVisualization } from './types'; + +export const isColumnWithMeta = (column: Column): column is ColumnWithMeta => { + if ((column as ColumnWithMeta).meta) { + return true; + } + return false; +}; + +export const excludeMetaFromColumn = (column: Column) => { + if (isColumnWithMeta(column)) { + const { meta, ...rest } = column; + return rest; + } + return column; +}; + +export const convertToLens: ConvertPieToLensVisualization = async (vis, timefilter) => { + if (!timefilter) { + return null; + } + + const dataViews = getDataViewsStart(); + const dataView = await getDataViewByIndexPatternId(vis.data.indexPattern?.id, dataViews); + + if (!dataView) { + return null; + } + + const { getColumnsFromVis } = await convertToLensModule; + const result = getColumnsFromVis(vis, timefilter, dataView, { + buckets: [], + splits: ['segment'], + unsupported: ['split_row', 'split_column'], + }); + + if (result === null) { + return null; + } + + // doesn't support more than three split slice levels + // doesn't support pie without at least one split slice + if (result.buckets.length > 3 || !result.buckets.length) { + return null; + } + + const layerId = uuid(); + + const indexPatternId = dataView.id!; + return { + type: 'lnsPie', + layers: [ + { + indexPatternId, + layerId, + columns: result.columns.map(excludeMetaFromColumn), + columnOrder: [], + }, + ], + configuration: getConfiguration(layerId, vis, result), + indexPatternIds: [indexPatternId], + }; +}; diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/types.ts b/src/plugins/vis_types/pie/public/convert_to_lens/types.ts new file mode 100644 index 000000000000..b190a4c89130 --- /dev/null +++ b/src/plugins/vis_types/pie/public/convert_to_lens/types.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TimefilterContract } from '@kbn/data-plugin/public'; +import { PartitionVisParams } from '@kbn/expression-partition-vis-plugin/common'; +import { + NavigateToLensContext, + PartitionVisConfiguration, +} from '@kbn/visualizations-plugin/common'; +import { Vis } from '@kbn/visualizations-plugin/public'; + +export type ConvertPieToLensVisualization = ( + vis: Vis, + timefilter?: TimefilterContract +) => Promise | null>; diff --git a/src/plugins/vis_types/pie/public/plugin.ts b/src/plugins/vis_types/pie/public/plugin.ts index 480cf0c49db6..b4a8c0e3a2a6 100644 --- a/src/plugins/vis_types/pie/public/plugin.ts +++ b/src/plugins/vis_types/pie/public/plugin.ts @@ -6,13 +6,15 @@ * Side Public License, v 1. */ -import { CoreSetup, DocLinksStart, ThemeServiceStart } from '@kbn/core/public'; +import { CoreSetup, CoreStart, DocLinksStart, ThemeServiceStart } from '@kbn/core/public'; import { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { LEGACY_PIE_CHARTS_LIBRARY } from '../common'; import { pieVisType } from './vis_type'; +import { setDataViewsStart } from './services'; /** @internal */ export interface VisTypePieSetupDependencies { @@ -21,6 +23,11 @@ export interface VisTypePieSetupDependencies { usageCollection: UsageCollectionSetup; } +/** @internal */ +export interface VisTypePieStartDependencies { + dataViews: DataViewsPublicPluginStart; +} + /** @internal */ export interface VisTypePiePluginStartDependencies { data: DataPublicPluginStart; @@ -53,5 +60,7 @@ export class VisTypePiePlugin { return {}; } - start() {} + start(core: CoreStart, { dataViews }: VisTypePieStartDependencies) { + setDataViewsStart(dataViews); + } } diff --git a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts index 3525b7b6fbc0..035432de9ad2 100644 --- a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts @@ -9,6 +9,8 @@ import { LegendDisplay } from '@kbn/expression-partition-vis-plugin/common'; import { LegendSize } from '@kbn/visualizations-plugin/common'; +const mockUiStateGet = jest.fn().mockReturnValue(() => false); + export const samplePieVis = { type: { name: 'pie', @@ -1353,5 +1355,6 @@ export const samplePieVis = { vis: { legendOpen: false, }, + get: mockUiStateGet, }, }; diff --git a/src/plugins/vis_types/pie/public/services.ts b/src/plugins/vis_types/pie/public/services.ts new file mode 100644 index 000000000000..736ad70d4941 --- /dev/null +++ b/src/plugins/vis_types/pie/public/services.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; + +export const [getDataViewsStart, setDataViewsStart] = + createGetterSetter('dataViews'); diff --git a/src/plugins/vis_types/pie/public/vis_type/pie.ts b/src/plugins/vis_types/pie/public/vis_type/pie.ts index 6d48507cf47a..8d4b7b6828e3 100644 --- a/src/plugins/vis_types/pie/public/vis_type/pie.ts +++ b/src/plugins/vis_types/pie/public/vis_type/pie.ts @@ -21,6 +21,7 @@ import { DEFAULT_PERCENT_DECIMALS } from '../../common'; import { PieTypeProps } from '../types'; import { toExpressionAst } from '../to_ast'; import { getPieOptions } from '../editor/components'; +import { convertToLens } from '../convert_to_lens'; export const getPieVisTypeDefinition = ({ showElasticChartsOptions = false, @@ -123,4 +124,10 @@ export const getPieVisTypeDefinition = ({ }, hierarchicalData: true, requiresSearch: true, + navigateToLens: async (vis, timefilter) => (vis ? convertToLens(vis, timefilter) : null), + getExpressionVariables: async (vis, timeFilter) => { + return { + canNavigateToLens: Boolean(vis?.params ? await convertToLens(vis, timeFilter) : null), + }; + }, }); diff --git a/src/plugins/visualizations/common/convert_to_lens/constants.ts b/src/plugins/visualizations/common/convert_to_lens/constants.ts index fa92b881033c..12ed815bc724 100644 --- a/src/plugins/visualizations/common/convert_to_lens/constants.ts +++ b/src/plugins/visualizations/common/convert_to_lens/constants.ts @@ -36,6 +36,70 @@ export const OperationsWithReferences = { export const Operations = { ...OperationsWithSourceField, ...OperationsWithReferences } as const; +export const PartitionChartTypes = { + PIE: 'pie', + DONUT: 'donut', + TREEMAP: 'treemap', + MOSAIC: 'mosaic', + WAFFLE: 'waffle', +} as const; + +export const CategoryDisplayTypes = { + DEFAULT: 'default', + INSIDE: 'inside', + HIDE: 'hide', +} as const; + +export const NumberDisplayTypes = { + HIDDEN: 'hidden', + PERCENT: 'percent', + VALUE: 'value', +} as const; + +export const LegendDisplayTypes = { + DEFAULT: 'default', + SHOW: 'show', + HIDE: 'hide', +} as const; + +export const LayerTypes = { + DATA: 'data', + REFERENCELINE: 'referenceLine', + ANNOTATIONS: 'annotations', +} as const; + +export const XYCurveTypes = { + LINEAR: 'LINEAR', + CURVE_MONOTONE_X: 'CURVE_MONOTONE_X', + CURVE_STEP_AFTER: 'CURVE_STEP_AFTER', +} as const; + +export const YAxisModes = { + AUTO: 'auto', + LEFT: 'left', + RIGHT: 'right', + BOTTOM: 'bottom', +} as const; + +export const SeriesTypes = { + BAR: 'bar', + LINE: 'line', + AREA: 'area', + BAR_STACKED: 'bar_stacked', + AREA_STACKED: 'area_stacked', + BAR_HORIZONTAL: 'bar_horizontal', + BAR_PERCENTAGE_STACKED: 'bar_percentage_stacked', + BAR_HORIZONTAL_STACKED: 'bar_horizontal_stacked', + AREA_PERCENTAGE_STACKED: 'area_percentage_stacked', + BAR_HORIZONTAL_PERCENTAGE_STACKED: 'bar_horizontal_percentage_stacked', +} as const; + +export const FillTypes = { + NONE: 'none', + ABOVE: 'above', + BELOW: 'below', +} as const; + export const RANGE_MODES = { Range: 'range', Histogram: 'histogram', diff --git a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts index fc1e440db350..a6a771d07e7a 100644 --- a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts +++ b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts @@ -11,43 +11,27 @@ import { $Values } from '@kbn/utility-types'; import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import { KibanaQueryOutput } from '@kbn/data-plugin/common'; import { LegendSize } from '../../constants'; - -export const XYCurveTypes = { - LINEAR: 'LINEAR', - CURVE_MONOTONE_X: 'CURVE_MONOTONE_X', - CURVE_STEP_AFTER: 'CURVE_STEP_AFTER', -} as const; - -export const YAxisModes = { - AUTO: 'auto', - LEFT: 'left', - RIGHT: 'right', - BOTTOM: 'bottom', -} as const; - -export const SeriesTypes = { - BAR: 'bar', - LINE: 'line', - AREA: 'area', - BAR_STACKED: 'bar_stacked', - AREA_STACKED: 'area_stacked', - BAR_HORIZONTAL: 'bar_horizontal', - BAR_PERCENTAGE_STACKED: 'bar_percentage_stacked', - BAR_HORIZONTAL_STACKED: 'bar_horizontal_stacked', - AREA_PERCENTAGE_STACKED: 'area_percentage_stacked', - BAR_HORIZONTAL_PERCENTAGE_STACKED: 'bar_horizontal_percentage_stacked', -} as const; - -export const FillTypes = { - NONE: 'none', - ABOVE: 'above', - BELOW: 'below', -} as const; +import { + CategoryDisplayTypes, + PartitionChartTypes, + NumberDisplayTypes, + LegendDisplayTypes, + FillTypes, + SeriesTypes, + YAxisModes, + XYCurveTypes, + LayerTypes, +} from '../constants'; export type FillType = $Values; export type SeriesType = $Values; export type YAxisMode = $Values; export type XYCurveType = $Values; +export type PartitionChartType = $Values; +export type CategoryDisplayType = $Values; +export type NumberDisplayType = $Values; +export type LegendDisplayType = $Values; +export type LayerType = $Values; export interface AxisExtentConfig { mode: 'full' | 'custom' | 'dataBounds'; @@ -217,4 +201,34 @@ export interface MetricVisConfiguration { maxCols?: number; } -export type Configuration = XYConfiguration | TableVisConfiguration | MetricVisConfiguration; +export interface PartitionLayerState { + layerId: string; + layerType: LayerType; + primaryGroups: string[]; + secondaryGroups?: string[]; + metric?: string; + collapseFns?: Record; + numberDisplay: NumberDisplayType; + categoryDisplay: CategoryDisplayType; + legendDisplay: LegendDisplayType; + legendPosition?: Position; + showValuesInLegend?: boolean; + nestedLegend?: boolean; + percentDecimals?: number; + emptySizeRatio?: number; + legendMaxLines?: number; + legendSize?: LegendSize; + truncateLegend?: boolean; +} + +export interface PartitionVisConfiguration { + shape: PartitionChartType; + layers: PartitionLayerState[]; + palette?: PaletteOutput; +} + +export type Configuration = + | XYConfiguration + | TableVisConfiguration + | PartitionVisConfiguration + | MetricVisConfiguration; diff --git a/src/plugins/visualizations/public/convert_to_lens/schemas.ts b/src/plugins/visualizations/public/convert_to_lens/schemas.ts index 372e434ca886..e9b467074b6f 100644 --- a/src/plugins/visualizations/public/convert_to_lens/schemas.ts +++ b/src/plugins/visualizations/public/convert_to_lens/schemas.ts @@ -24,14 +24,26 @@ import { sortColumns, } from './utils'; +const areVisSchemasValid = (visSchemas: Schemas, unsupported: Array) => { + const usedUnsupportedSchemas = unsupported.filter( + (schema) => visSchemas[schema] && visSchemas[schema]?.length + ); + return !usedUnsupportedSchemas.length; +}; + export const getColumnsFromVis = ( vis: Vis, timefilter: TimefilterContract, dataView: DataView, - { splits, buckets }: { splits: Array; buckets: Array } = { - splits: [], - buckets: [], - }, + { + splits = [], + buckets = [], + unsupported = [], + }: { + splits?: Array; + buckets?: Array; + unsupported?: Array; + } = {}, config?: { dropEmptyRowsInDateHistogram?: boolean; } @@ -41,7 +53,7 @@ export const getColumnsFromVis = ( timeRange: timefilter.getAbsoluteTime(), }); - if (!isValidVis(visSchemas)) { + if (!isValidVis(visSchemas) || !areVisSchemasValid(visSchemas, unsupported)) { return null; } @@ -111,8 +123,8 @@ export const getColumnsFromVis = ( const columnsWithoutReferenced = getColumnsWithoutReferenced(columns); return { - metrics: getColumnIds(metrics), - buckets: getColumnIds([...bucketColumns, ...splitBucketColumns, ...customBucketColumns]), + metrics: getColumnIds(columnsWithoutReferenced.filter((с) => !с.isBucketed)), + buckets: getColumnIds(columnsWithoutReferenced.filter((c) => c.isBucketed)), bucketCollapseFn: getBucketCollapseFn(visSchemas.metric, customBucketColumns), columnsWithoutReferenced, columns, diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx index 500a5e5b34d4..055b326b8f19 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx @@ -96,6 +96,7 @@ const TopNav = ({ [doReload] ); + const uiStateJSON = useMemo(() => vis.uiState.toJSON(), [vis.uiState]); useEffect(() => { const asyncGetTriggerContext = async () => { if (vis.type.navigateToLens) { @@ -107,7 +108,14 @@ const TopNav = ({ } }; asyncGetTriggerContext(); - }, [services.data.query.timefilter.timefilter, vis, vis.type, vis.params, vis.data.indexPattern]); + }, [ + services.data.query.timefilter.timefilter, + vis, + vis.type, + vis.params, + uiStateJSON?.vis, + vis.data.indexPattern, + ]); const displayEditInLensItem = Boolean(vis.type.navigateToLens && editInLensConfig); const config = useMemo(() => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index 2e81f9443ff1..dc09baf01fb2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -534,12 +534,8 @@ function getTopSuggestion( ); }); - // We prefer unchanged or reduced suggestions when switching - // charts since that allows you to switch from A to B and back - // to A with the greatest chance of preserving your original state. return ( - suggestions.find((s) => s.changeType === 'unchanged') || - suggestions.find((s) => s.changeType === 'reduced') || + suggestions.find((s) => s.changeType === 'unchanged' || s.changeType === 'reduced') || suggestions[0] ); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 860c272d9ee1..382c11bda545 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1774,7 +1774,7 @@ describe('IndexPattern Data Source suggestions', () => { state: expect.objectContaining({ layers: { test: expect.objectContaining({ - columnOrder: ['column-id-3', 'column-id-2', 'column-id-1'], + columnOrder: ['column-id-2', 'column-id-3', 'column-id-1'], columns: { 'column-id-1': expect.objectContaining({ operationType: 'count', @@ -1804,10 +1804,10 @@ describe('IndexPattern Data Source suggestions', () => { isMultiRow: true, columns: [ expect.objectContaining({ - columnId: 'column-id-3', + columnId: 'column-id-2', }), expect.objectContaining({ - columnId: 'column-id-2', + columnId: 'column-id-3', }), expect.objectContaining({ columnId: 'column-id-1', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index fed70d2970b1..8503c369f4ec 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -341,6 +341,7 @@ function createNewLayerWithMetricAggregationFromVizEditor( newLayer = insertNewColumn({ ...column, layer: newLayer, + respectOrder: true, }); } }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 2d8b41ce866b..b3f1b3bc7d5f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -68,6 +68,7 @@ interface ColumnChange { columnParams?: Record; initialParams?: { params: Record }; // TODO: bind this to the op parameter references?: Array>; + respectOrder?: boolean; } interface ColumnCopy { @@ -362,6 +363,7 @@ export function insertNewColumn({ columnParams, initialParams, references, + respectOrder, }: ColumnChange): IndexPatternLayer { const operationDefinition = operationDefinitionMap[op]; @@ -394,7 +396,14 @@ export function insertNewColumn({ : operationDefinition.buildColumn({ ...baseOptions, layer }); return updateDefaultLabels( - addOperationFn(layer, buildColumnFn, columnId, visualizationGroups, targetGroup), + addOperationFn( + layer, + buildColumnFn, + columnId, + visualizationGroups, + targetGroup, + respectOrder + ), indexPattern ); } @@ -445,7 +454,14 @@ export function insertNewColumn({ ) : operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, referenceIds }); return updateDefaultLabels( - addOperationFn(tempLayer, buildColumnFn, columnId, visualizationGroups, targetGroup), + addOperationFn( + tempLayer, + buildColumnFn, + columnId, + visualizationGroups, + targetGroup, + respectOrder + ), indexPattern ); } @@ -468,7 +484,8 @@ export function insertNewColumn({ operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), columnId, visualizationGroups, - targetGroup + targetGroup, + respectOrder ), indexPattern ); @@ -508,7 +525,7 @@ export function insertNewColumn({ const isBucketed = Boolean(possibleOperation.isBucketed); const addOperationFn = isBucketed ? addBucket : addMetric; return updateDefaultLabels( - addOperationFn(layer, newColumn, columnId, visualizationGroups, targetGroup), + addOperationFn(layer, newColumn, columnId, visualizationGroups, targetGroup, respectOrder), indexPattern ); } @@ -1154,7 +1171,8 @@ function addBucket( column: BaseIndexPatternColumn, addedColumnId: string, visualizationGroups: VisualizationDimensionGroupConfig[], - targetGroup?: string + targetGroup?: string, + respectOrder?: boolean ): IndexPatternLayer { const [buckets, metrics] = partition( layer.columnOrder, @@ -1166,7 +1184,7 @@ function addBucket( ); let updatedColumnOrder: string[] = []; - if (oldDateHistogramIndex > -1 && column.operationType === 'terms') { + if (oldDateHistogramIndex > -1 && column.operationType === 'terms' && !respectOrder) { // Insert the new terms bucket above the first date histogram updatedColumnOrder = [ ...buckets.slice(0, oldDateHistogramIndex), diff --git a/x-pack/plugins/lens/public/trigger_actions/visualize_agg_based_vis_actions.ts b/x-pack/plugins/lens/public/trigger_actions/visualize_agg_based_vis_actions.ts index 2d2b329a7ea6..a5300d550f2c 100644 --- a/x-pack/plugins/lens/public/trigger_actions/visualize_agg_based_vis_actions.ts +++ b/x-pack/plugins/lens/public/trigger_actions/visualize_agg_based_vis_actions.ts @@ -35,7 +35,7 @@ export const visualizeAggBasedVisAction = (application: ApplicationStart) => type: ACTION_CONVERT_TO_LENS, payload, originatingApp: i18n.translate('xpack.lens.AggBasedLabel', { - defaultMessage: 'Aggregation based visualization', + defaultMessage: 'aggregation based visualization', }), }, }); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index af2dcc601c99..fc1dc5ed1661 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -17,7 +17,7 @@ import type { IInterpreterRenderHandlers, Datatable, } from '@kbn/expressions-plugin/public'; -import type { NavigateToLensContext } from '@kbn/visualizations-plugin/common'; +import type { Configuration, NavigateToLensContext } from '@kbn/visualizations-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/public'; import type { Query } from '@kbn/es-query'; import type { @@ -217,13 +217,13 @@ export interface InitializationOptions { isFullEditor?: boolean; } -export type VisualizeEditorContext = { +export type VisualizeEditorContext = { savedObjectId?: string; embeddableId?: string; vizEditorOriginatingAppUrl?: string; originatingApp?: string; isVisualizeAction: boolean; -} & NavigateToLensContext; +} & NavigateToLensContext; export interface GetDropPropsArgs { state: T; @@ -1129,7 +1129,7 @@ export interface Visualization { getSuggestionFromConvertToLensContext?: ( props: VisualizationStateFromContextChangeProps - ) => Suggestion; + ) => Suggestion | undefined; } // Use same technique as TriggerContext diff --git a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx index 72bf5812b715..4ef58c22969e 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx @@ -161,11 +161,19 @@ export const getDatatableVisualization = ({ }, }); + const changeType = table.changeType; + const changeFactor = + changeType === 'reduced' || changeType === 'layers' + ? 0.3 + : changeType === 'unchanged' + ? 0.5 + : 1; + return [ { title, // table with >= 10 columns will have a score of 0.4, fewer columns reduce score - score: (Math.min(table.columns.length, 10) / 10) * 0.4, + score: (Math.min(table.columns.length, 10) / 10) * 0.4 * changeFactor, state: { ...(state || {}), layerId: table.layerId, diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index eea673dc531d..dc0ce54b5bc0 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -93,7 +93,7 @@ describe('suggestions', () => { ).toHaveLength(0); }); - it('should reject date operations', () => { + it('should hide date operations', () => { expect( suggestions({ table: { @@ -118,11 +118,17 @@ describe('suggestions', () => { }, state: undefined, keptLayerIds: ['first'], - }) - ).toHaveLength(0); + }).map((s) => [s.hide, s.score]) + ).toEqual([ + [true, 0], + [true, 0], + [true, 0], + [true, 0], + [true, 0], + ]); }); - it('should reject histogram operations', () => { + it('should hide histogram operations', () => { expect( suggestions({ table: { @@ -147,8 +153,14 @@ describe('suggestions', () => { }, state: undefined, keptLayerIds: ['first'], - }) - ).toHaveLength(0); + }).map((s) => [s.hide, s.score]) + ).toEqual([ + [true, 0], + [true, 0], + [true, 0], + [true, 0], + [true, 0], + ]); }); it('should not reject histogram operations in case of switching between partition charts', () => { diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index 36e367ca3ad6..9f2d0920983a 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -29,15 +29,10 @@ function hasIntervalScale(columns: TableSuggestionColumn[]) { } function shouldReject({ table, keptLayerIds, state }: SuggestionRequest) { - // Histograms are not good for pi. But we should not reject them on switching between partition charts. - const shouldRejectIntervals = - state?.shape && isPartitionShape(state.shape) ? false : hasIntervalScale(table.columns); - return ( keptLayerIds.length > 1 || (keptLayerIds.length && table.layerId !== keptLayerIds[0]) || table.changeType === 'reorder' || - shouldRejectIntervals || table.columns.some((col) => col.operation.isStaticValue) ); } @@ -111,6 +106,10 @@ export function suggestions({ const results: Array> = []; + // Histograms are not good for pi. But we should not hide suggestion on switching between partition charts. + const shouldHideSuggestion = + state?.shape && isPartitionShape(state.shape) ? false : hasIntervalScale(table.columns); + if ( groups.length <= PartitionChartsMeta.pie.maxBuckets && !hasCustomSuggestionsExists(subVisualizationId) @@ -309,11 +308,11 @@ export function suggestions({ return [...results] .map((suggestion) => ({ ...suggestion, - score: suggestion.score + 0.05 * groups.length, + score: shouldHideSuggestion ? 0 : suggestion.score + 0.05 * groups.length, })) .sort((a, b) => b.score - a.score) .map((suggestion) => ({ ...suggestion, - hide: incompleteConfiguration || suggestion.hide, + hide: shouldHideSuggestion || incompleteConfiguration || suggestion.hide, })); } diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 6a58d46b34ca..a60578aa1f0b 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -14,11 +14,14 @@ import { ThemeServiceStart } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { EuiSpacer } from '@elastic/eui'; +import { PartitionVisConfiguration } from '@kbn/visualizations-plugin/common/convert_to_lens'; import type { Visualization, OperationMetadata, AccessorConfig, VisualizationDimensionGroupConfig, + Suggestion, + VisualizeEditorContext, } from '../../types'; import { getSortedGroups, toExpression, toPreviewExpression } from './to_expression'; import { CategoryDisplay, layerTypes, LegendDisplay, NumberDisplay } from '../../../common'; @@ -27,6 +30,17 @@ import { PartitionChartsMeta } from './partition_charts_meta'; import { DimensionEditor, PieToolbar } from './toolbar'; import { checkTableForContainsSmallValues } from './render_helpers'; import { PieChartTypes, PieLayerState, PieVisualizationState } from '../../../common'; +import { IndexPatternLayer } from '../..'; + +interface DatatableDatasourceState { + [prop: string]: unknown; + layers: IndexPatternLayer[]; +} + +export interface PartitionSuggestion extends Suggestion { + datasourceState: DatatableDatasourceState; + visualizationState: PieVisualizationState; +} function newLayerState(layerId: string): PieLayerState { return { @@ -42,6 +56,12 @@ function newLayerState(layerId: string): PieLayerState { }; } +function isPartitionVisConfiguration( + context: VisualizeEditorContext +): context is VisualizeEditorContext { + return context.type === 'lnsPie'; +} + const bucketedOperations = (op: OperationMetadata) => op.isBucketed; const numberMetricOperations = (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number' && !op.isStaticValue; @@ -422,6 +442,29 @@ export const getPieVisualization = ({ return warningMessages; }, + getSuggestionFromConvertToLensContext(props) { + const context = props.context; + if (!isPartitionVisConfiguration(context)) { + return; + } + if (!props.suggestions.length) { + return; + } + const suggestionByShape = (props.suggestions as PartitionSuggestion[]).find( + (suggestion) => suggestion.visualizationState.shape === context.configuration.shape + ); + if (!suggestionByShape) { + return; + } + return { + ...suggestionByShape, + visualizationState: { + ...suggestionByShape.visualizationState, + ...context.configuration, + }, + }; + }, + getErrorMessages(state) { const hasTooManyBucketDimensions = state.layers .map( 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 1184712ae033..b63bfeb5fbd9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -615,7 +615,12 @@ function getScore( changeType: TableChangeType ) { // Unchanged table suggestions half the score because the underlying data doesn't change - const changeFactor = changeType === 'unchanged' ? 0.5 : 1; + const changeFactor = + changeType === 'reduced' || changeType === 'layers' + ? 0.3 + : changeType === 'unchanged' + ? 0.5 + : 1; // chart with multiple y values and split series will have a score of 1, single y value and no split series reduce score return (((yValues.length > 1 ? 2 : 1) + (splitBy ? 1 : 0)) / 3) * changeFactor; } diff --git a/x-pack/test/functional/apps/lens/group3/index.ts b/x-pack/test/functional/apps/lens/group3/index.ts index 44365d5333d1..627e9d560ca2 100644 --- a/x-pack/test/functional/apps/lens/group3/index.ts +++ b/x-pack/test/functional/apps/lens/group3/index.ts @@ -86,7 +86,7 @@ export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext loadTestFile(require.resolve('./error_handling')); loadTestFile(require.resolve('./lens_tagging')); loadTestFile(require.resolve('./lens_reporting')); - loadTestFile(require.resolve('./tsvb_open_in_lens')); + loadTestFile(require.resolve('./open_in_lens')); // keep these two last in the group in this order because they are messing with the default saved objects loadTestFile(require.resolve('./rollup')); loadTestFile(require.resolve('./no_data')); diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts new file mode 100644 index 000000000000..b279f0d8a93c --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Agg based Vis to Lens', function () { + loadTestFile(require.resolve('./pie')); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/pie.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/pie.ts new file mode 100644 index 000000000000..1640a6a14631 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/agg_based/pie.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualize, visEditor, lens, timePicker, header } = getPageObjects([ + 'visualize', + 'lens', + 'visEditor', + 'timePicker', + 'header', + ]); + + const testSubjects = getService('testSubjects'); + const pieChart = getService('pieChart'); + + describe('Pie', function describeIndexTests() { + const isNewChartsLibraryEnabled = true; + + before(async () => { + await visualize.initTests(isNewChartsLibraryEnabled); + }); + + beforeEach(async () => { + await visualize.navigateToNewAggBasedVisualization(); + await visualize.clickPieChart(); + await visualize.clickNewSearch(); + await timePicker.setDefaultAbsoluteRange(); + }); + + it('should hide the "Edit Visualization in Lens" menu item if no split slices were defined', async () => { + const button = await testSubjects.exists('visualizeEditInLensButton'); + expect(button).to.eql(false); + }); + + it('should show the "Edit Visualization in Lens" menu item', async () => { + await visEditor.clickBucket('Split slices'); + await visEditor.selectAggregation('Terms'); + await visEditor.selectField('machine.os.raw'); + await header.waitUntilLoadingHasFinished(); + await visEditor.clickGo(isNewChartsLibraryEnabled); + + const button = await testSubjects.exists('visualizeEditInLensButton'); + expect(button).to.eql(true); + }); + + it('should convert to Lens', async () => { + const expectedTableData = ['ios', 'osx', 'win 7', 'win 8', 'win xp']; + await visEditor.clickBucket('Split slices'); + await visEditor.selectAggregation('Terms'); + await visEditor.selectField('machine.os.raw'); + await header.waitUntilLoadingHasFinished(); + await visEditor.clickGo(isNewChartsLibraryEnabled); + + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('partitionVisChart'); + + await pieChart.expectPieChartLabels(expectedTableData, isNewChartsLibraryEnabled); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/index.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/index.ts new file mode 100644 index 000000000000..b1d5a1cbb3c5 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Open in Lens', function () { + loadTestFile(require.resolve('./tsvb')); + loadTestFile(require.resolve('./agg_based')); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/dashboard.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/dashboard.ts new file mode 100644 index 000000000000..bd74e109326b --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/dashboard.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualize, visualBuilder, lens, timeToVisualize, dashboard, canvas } = getPageObjects([ + 'visualBuilder', + 'visualize', + 'lens', + 'timeToVisualize', + 'dashboard', + 'canvas', + ]); + + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const panelActions = getService('dashboardPanelActions'); + const dashboardAddPanel = getService('dashboardAddPanel'); + + describe('Dashboard to TSVB to Lens', function describeIndexTests() { + before(async () => { + await visualize.initTests(); + }); + + it('should convert a by value TSVB viz to a Lens viz', async () => { + await visualize.navigateToNewVisualization(); + await visualize.clickVisualBuilder(); + await visualBuilder.checkVisualBuilderIsPresent(); + await visualBuilder.resetPage(); + await testSubjects.click('visualizeSaveButton'); + + await timeToVisualize.saveFromModal('My TSVB to Lens viz 1', { + addToDashboard: 'new', + saveToLibrary: false, + }); + + await dashboard.waitForRenderComplete(); + const originalEmbeddableCount = await canvas.getEmbeddableCount(); + await panelActions.openContextMenu(); + await panelActions.clickEdit(); + + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('xyVisChart'); + await retry.try(async () => { + const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); + expect(await dimensions[1].getVisibleText()).to.be('Count of records'); + }); + + await lens.saveAndReturn(); + await retry.try(async () => { + const embeddableCount = await canvas.getEmbeddableCount(); + expect(embeddableCount).to.eql(originalEmbeddableCount); + }); + await panelActions.removePanel(); + }); + + it('should convert a by reference TSVB viz to a Lens viz', async () => { + await dashboardAddPanel.clickEditorMenuButton(); + await dashboardAddPanel.clickVisType('metrics'); + await testSubjects.click('visualizesaveAndReturnButton'); + // save it to library + const originalPanel = await testSubjects.find('embeddablePanelHeading-'); + await panelActions.saveToLibrary('My TSVB to Lens viz 2', originalPanel); + + await dashboard.waitForRenderComplete(); + const originalEmbeddableCount = await canvas.getEmbeddableCount(); + await panelActions.openContextMenu(); + await panelActions.clickEdit(); + + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('legacyMtrVis'); + await retry.try(async () => { + const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); + expect(await dimensions[1].getVisibleText()).to.be('Count of records'); + }); + + await lens.saveAndReturn(); + await retry.try(async () => { + const embeddableCount = await canvas.getEmbeddableCount(); + expect(embeddableCount).to.eql(originalEmbeddableCount); + }); + + const panel = await testSubjects.find(`embeddablePanelHeading-`); + const descendants = await testSubjects.findAllDescendant( + 'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION', + panel + ); + expect(descendants.length).to.equal(0); + + await panelActions.removePanel(); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/index.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/index.ts new file mode 100644 index 000000000000..c786ed22c6a1 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('TSVB to Lens', function () { + loadTestFile(require.resolve('./metric')); + loadTestFile(require.resolve('./timeseries')); + loadTestFile(require.resolve('./dashboard')); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/metric.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/metric.ts new file mode 100644 index 000000000000..273473b67a31 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/metric.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualize, visualBuilder, lens } = getPageObjects(['visualBuilder', 'visualize', 'lens']); + + const testSubjects = getService('testSubjects'); + + describe('Metric', function describeIndexTests() { + before(async () => { + await visualize.initTests(); + }); + + beforeEach(async () => { + await visualize.navigateToNewVisualization(); + await visualize.clickVisualBuilder(); + await visualBuilder.checkVisualBuilderIsPresent(); + await visualBuilder.resetPage(); + await visualBuilder.clickMetric(); + await visualBuilder.clickDataTab('metric'); + }); + + it('should show the "Edit Visualization in Lens" menu item', async () => { + const button = await testSubjects.exists('visualizeEditInLensButton'); + expect(button).to.eql(true); + }); + + it('should convert to Lens', async () => { + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('mtrVis'); + + const metricData = await lens.getMetricVisualizationData(); + expect(metricData[0].title).to.eql('Count of records'); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/timeseries.ts b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/timeseries.ts new file mode 100644 index 000000000000..17e3a6b1bee8 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/open_in_lens/tsvb/timeseries.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualize, visualBuilder, lens, header } = getPageObjects([ + 'visualBuilder', + 'visualize', + 'header', + 'lens', + ]); + + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const find = getService('find'); + const filterBar = getService('filterBar'); + const queryBar = getService('queryBar'); + + describe('Time Series', function describeIndexTests() { + before(async () => { + await visualize.initTests(); + }); + + it('should show the "Edit Visualization in Lens" menu item for a count aggregation', async () => { + await visualize.navigateToNewVisualization(); + await visualize.clickVisualBuilder(); + await visualBuilder.checkVisualBuilderIsPresent(); + await visualBuilder.resetPage(); + const isMenuItemVisible = await find.existsByCssSelector( + '[data-test-subj="visualizeEditInLensButton"]' + ); + expect(isMenuItemVisible).to.be(true); + }); + + it('visualizes field to Lens and loads fields to the dimesion editor', async () => { + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('xyVisChart'); + await retry.try(async () => { + const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); + expect(dimensions).to.have.length(2); + expect(await dimensions[0].getVisibleText()).to.be('@timestamp'); + expect(await dimensions[1].getVisibleText()).to.be('Count of records'); + }); + }); + + it('navigates back to TSVB when the Back button is clicked', async () => { + const goBackBtn = await testSubjects.find('lnsApp_goBackToAppButton'); + goBackBtn.click(); + await visualBuilder.checkVisualBuilderIsPresent(); + await retry.try(async () => { + const actualCount = await visualBuilder.getRhythmChartLegendValue(); + expect(actualCount).to.be('56'); + }); + }); + + it('should preserve app filters in lens', async () => { + await filterBar.addFilter('extension', 'is', 'css'); + await header.waitUntilLoadingHasFinished(); + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('xyVisChart'); + + expect(await filterBar.hasFilter('extension', 'css')).to.be(true); + }); + + it('should preserve query in lens', async () => { + const goBackBtn = await testSubjects.find('lnsApp_goBackToAppButton'); + goBackBtn.click(); + await visualBuilder.checkVisualBuilderIsPresent(); + await queryBar.setQuery('machine.os : ios'); + await queryBar.submitQuery(); + await header.waitUntilLoadingHasFinished(); + const button = await testSubjects.find('visualizeEditInLensButton'); + await button.click(); + await lens.waitForVisualization('xyVisChart'); + + expect(await queryBar.getQueryString()).to.equal('machine.os : ios'); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts b/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts deleted file mode 100644 index 173ab1c4fdf0..000000000000 --- a/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts +++ /dev/null @@ -1,192 +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 '@kbn/expect'; - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const { visualize, visualBuilder, header, lens, timeToVisualize, dashboard, canvas } = - getPageObjects([ - 'visualBuilder', - 'visualize', - 'header', - 'lens', - 'timeToVisualize', - 'dashboard', - 'canvas', - ]); - const testSubjects = getService('testSubjects'); - const find = getService('find'); - const dashboardAddPanel = getService('dashboardAddPanel'); - const panelActions = getService('dashboardPanelActions'); - const retry = getService('retry'); - const filterBar = getService('filterBar'); - const queryBar = getService('queryBar'); - - describe('TSVB to Lens', function describeIndexTests() { - before(async () => { - await visualize.initTests(); - }); - - describe('Time Series', () => { - it('should show the "Edit Visualization in Lens" menu item for a count aggregation', async () => { - await visualize.navigateToNewVisualization(); - await visualize.clickVisualBuilder(); - await visualBuilder.checkVisualBuilderIsPresent(); - await visualBuilder.resetPage(); - const isMenuItemVisible = await find.existsByCssSelector( - '[data-test-subj="visualizeEditInLensButton"]' - ); - expect(isMenuItemVisible).to.be(true); - }); - - it('visualizes field to Lens and loads fields to the dimesion editor', async () => { - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('xyVisChart'); - await retry.try(async () => { - const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); - expect(dimensions).to.have.length(2); - expect(await dimensions[0].getVisibleText()).to.be('@timestamp'); - expect(await dimensions[1].getVisibleText()).to.be('Count of records'); - }); - }); - - it('navigates back to TSVB when the Back button is clicked', async () => { - const goBackBtn = await testSubjects.find('lnsApp_goBackToAppButton'); - goBackBtn.click(); - await visualBuilder.checkVisualBuilderIsPresent(); - await retry.try(async () => { - const actualCount = await visualBuilder.getRhythmChartLegendValue(); - expect(actualCount).to.be('56'); - }); - }); - - it('should preserve app filters in lens', async () => { - await filterBar.addFilter('extension', 'is', 'css'); - await header.waitUntilLoadingHasFinished(); - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('xyVisChart'); - - expect(await filterBar.hasFilter('extension', 'css')).to.be(true); - }); - - it('should preserve query in lens', async () => { - const goBackBtn = await testSubjects.find('lnsApp_goBackToAppButton'); - goBackBtn.click(); - await visualBuilder.checkVisualBuilderIsPresent(); - await queryBar.setQuery('machine.os : ios'); - await queryBar.submitQuery(); - await header.waitUntilLoadingHasFinished(); - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('xyVisChart'); - - expect(await queryBar.getQueryString()).to.equal('machine.os : ios'); - }); - }); - - describe('Metric', () => { - beforeEach(async () => { - await visualize.navigateToNewVisualization(); - await visualize.clickVisualBuilder(); - await visualBuilder.checkVisualBuilderIsPresent(); - await visualBuilder.resetPage(); - await visualBuilder.clickMetric(); - await visualBuilder.clickDataTab('metric'); - }); - - it('should show the "Edit Visualization in Lens" menu item', async () => { - const button = await testSubjects.exists('visualizeEditInLensButton'); - expect(button).to.eql(true); - }); - - it('should convert to Lens', async () => { - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('mtrVis'); - - const metricData = await lens.getMetricVisualizationData(); - expect(metricData[0].title).to.eql('Count of records'); - }); - }); - - describe('Dashboard to TSVB to Lens', () => { - it('should convert a by value TSVB viz to a Lens viz', async () => { - await visualize.navigateToNewVisualization(); - await visualize.clickVisualBuilder(); - await visualBuilder.checkVisualBuilderIsPresent(); - await visualBuilder.resetPage(); - await testSubjects.click('visualizeSaveButton'); - - await timeToVisualize.saveFromModal('My TSVB to Lens viz 1', { - addToDashboard: 'new', - saveToLibrary: false, - }); - - await dashboard.waitForRenderComplete(); - const originalEmbeddableCount = await canvas.getEmbeddableCount(); - await panelActions.openContextMenu(); - await panelActions.clickEdit(); - - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('xyVisChart'); - await retry.try(async () => { - const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); - expect(await dimensions[1].getVisibleText()).to.be('Count of records'); - }); - - await lens.saveAndReturn(); - await retry.try(async () => { - const embeddableCount = await canvas.getEmbeddableCount(); - expect(embeddableCount).to.eql(originalEmbeddableCount); - }); - await panelActions.removePanel(); - }); - - it('should convert a by reference TSVB viz to a Lens viz', async () => { - await dashboardAddPanel.clickEditorMenuButton(); - await dashboardAddPanel.clickVisType('metrics'); - await testSubjects.click('visualizesaveAndReturnButton'); - // save it to library - const originalPanel = await testSubjects.find('embeddablePanelHeading-'); - await panelActions.saveToLibrary('My TSVB to Lens viz 2', originalPanel); - - await dashboard.waitForRenderComplete(); - const originalEmbeddableCount = await canvas.getEmbeddableCount(); - await panelActions.openContextMenu(); - await panelActions.clickEdit(); - - const button = await testSubjects.find('visualizeEditInLensButton'); - await button.click(); - await lens.waitForVisualization('legacyMtrVis'); - await retry.try(async () => { - const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); - expect(await dimensions[1].getVisibleText()).to.be('Count of records'); - }); - - await lens.saveAndReturn(); - await retry.try(async () => { - const embeddableCount = await canvas.getEmbeddableCount(); - expect(embeddableCount).to.eql(originalEmbeddableCount); - }); - - const panel = await testSubjects.find(`embeddablePanelHeading-`); - const descendants = await testSubjects.findAllDescendant( - 'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION', - panel - ); - expect(descendants.length).to.equal(0); - - await panelActions.removePanel(); - }); - }); - }); -} From 6a0b2fd717e53c177fcc9fe3bad02c0f726e5d78 Mon Sep 17 00:00:00 2001 From: Michael Dokolin Date: Thu, 29 Sep 2022 08:38:58 +0200 Subject: [PATCH 096/185] [Expressions] Fix expressions chain invocation not to unsubscribe on error (#142105) --- .../common/execution/execution.test.ts | 57 +++++++ .../expressions/common/execution/execution.ts | 145 +++++++++--------- 2 files changed, 129 insertions(+), 73 deletions(-) diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 75a95035bb89..a5e03084a697 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -488,6 +488,63 @@ describe('Execution', () => { expect(spy.fn).toHaveBeenCalledTimes(0); }); + + test('continues execution when error state is gone', async () => { + testScheduler.run(({ cold, expectObservable, flush }) => { + const a = 1; + const b = 2; + const c = 3; + const observable$ = cold('abc|', { a, b, c }); + const flakyFn = jest + .fn() + .mockImplementationOnce((value) => value) + .mockImplementationOnce(() => { + throw new Error('Some error.'); + }) + .mockImplementationOnce((value) => value); + const spyFn = jest.fn((value) => value); + + const executor = createUnitTestExecutor(); + executor.registerFunction({ + name: 'observable', + args: {}, + help: '', + fn: () => observable$, + }); + executor.registerFunction({ + name: 'flaky', + args: {}, + help: '', + fn: (value) => flakyFn(value), + }); + executor.registerFunction({ + name: 'spy', + args: {}, + help: '', + fn: (value) => spyFn(value), + }); + + const result = executor.run('observable | flaky | spy', null, {}); + + expectObservable(result).toBe('abc|', { + a: { partial: true, result: a }, + b: { + partial: true, + result: { + type: 'error', + error: expect.objectContaining({ message: '[flaky] > Some error.' }), + }, + }, + c: { partial: false, result: c }, + }); + + flush(); + + expect(spyFn).toHaveBeenCalledTimes(2); + expect(spyFn).toHaveBeenNthCalledWith(1, a); + expect(spyFn).toHaveBeenNthCalledWith(2, c); + }); + }); }); describe('state', () => { diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index b4ef83389ce2..68cebaa65569 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -295,87 +295,86 @@ export class Execution< } invokeChain( - chainArr: ExpressionAstFunction[], + [head, ...tail]: ExpressionAstFunction[], input: unknown - ): Observable { + ): Observable { + if (!head) { + return of(input as ChainOutput); + } + return of(input).pipe( - ...(chainArr.map((link) => - switchMap((currentInput) => { - const { function: fnName, arguments: fnArgs } = link; - const fn = getByAlias( - this.state.get().functions, - fnName, - this.execution.params.namespace - ); + switchMap((currentInput) => { + const { function: fnName, arguments: fnArgs } = head; + const fn = getByAlias(this.state.get().functions, fnName, this.execution.params.namespace); + + if (!fn) { + throw createError({ + name: 'fn not found', + message: i18n.translate('expressions.execution.functionNotFound', { + defaultMessage: `Function {fnName} could not be found.`, + values: { + fnName, + }, + }), + }); + } - if (!fn) { - throw createError({ - name: 'fn not found', - message: i18n.translate('expressions.execution.functionNotFound', { - defaultMessage: `Function {fnName} could not be found.`, - values: { - fnName, - }, - }), - }); - } + if (fn.disabled) { + throw createError({ + name: 'fn is disabled', + message: i18n.translate('expressions.execution.functionDisabled', { + defaultMessage: `Function {fnName} is disabled.`, + values: { + fnName, + }, + }), + }); + } - if (fn.disabled) { - throw createError({ - name: 'fn is disabled', - message: i18n.translate('expressions.execution.functionDisabled', { - defaultMessage: `Function {fnName} is disabled.`, - values: { - fnName, - }, - }), - }); - } + if (fn.deprecated) { + this.logger?.warn(`Function '${fnName}' is deprecated`); + } - if (fn.deprecated) { - this.logger?.warn(`Function '${fnName}' is deprecated`); - } + if (this.execution.params.debug) { + head.debug = { + args: {}, + duration: 0, + fn: fn.name, + input: currentInput, + success: true, + }; + } - if (this.execution.params.debug) { - link.debug = { - args: {}, - duration: 0, - fn: fn.name, - input: currentInput, - success: true, - }; - } + const timeStart = this.execution.params.debug ? now() : 0; + + // `resolveArgs` returns an object because the arguments themselves might + // actually have `then` or `subscribe` methods which would be treated as a `Promise` + // or an `Observable` accordingly. + return this.resolveArgs(fn, currentInput, fnArgs).pipe( + tap((args) => this.execution.params.debug && Object.assign(head.debug, { args })), + switchMap((args) => this.invokeFunction(fn, currentInput, args)), + switchMap((output) => (getType(output) === 'error' ? throwError(output) : of(output))), + tap((output) => this.execution.params.debug && Object.assign(head.debug, { output })), + switchMap((output) => this.invokeChain(tail, output)), + catchError((rawError) => { + const error = createError(rawError); + error.error.message = `[${fnName}] > ${error.error.message}`; + + if (this.execution.params.debug) { + Object.assign(head.debug, { error, rawError, success: false }); + } - const timeStart = this.execution.params.debug ? now() : 0; - - // `resolveArgs` returns an object because the arguments themselves might - // actually have `then` or `subscribe` methods which would be treated as a `Promise` - // or an `Observable` accordingly. - return this.resolveArgs(fn, currentInput, fnArgs).pipe( - tap((args) => this.execution.params.debug && Object.assign(link.debug, { args })), - switchMap((args) => this.invokeFunction(fn, currentInput, args)), - switchMap((output) => (getType(output) === 'error' ? throwError(output) : of(output))), - tap((output) => this.execution.params.debug && Object.assign(link.debug, { output })), - catchError((rawError) => { - const error = createError(rawError); - error.error.message = `[${fnName}] > ${error.error.message}`; - - if (this.execution.params.debug) { - Object.assign(link.debug, { error, rawError, success: false }); - } - - return throwError(error); - }), - finalize(() => { - if (this.execution.params.debug) { - Object.assign(link.debug, { duration: now() - timeStart }); - } - }) - ); - }) - ) as Parameters['pipe']>), + return of(error); + }), + finalize(() => { + if (this.execution.params.debug) { + Object.assign(head.debug, { duration: now() - timeStart }); + } + }) + ); + }), catchError((error) => of(error)) - ) as Observable; + ); } invokeFunction( From e3bf5539a1a55e06b4076906c8c1420d2d00ae37 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Thu, 29 Sep 2022 09:05:12 +0100 Subject: [PATCH 097/185] [APM] Add e2e tests for storage explorer (#141959) * Refactor APM user creation and add tests for storage explorer --- .../storage_explorer/storage_explorer.cy.ts | 192 ++++++++++++++++++ .../apm/ftr_e2e/cypress/support/commands.ts | 12 +- .../apm/ftr_e2e/cypress/support/types.d.ts | 1 + .../apm/ftr_e2e/cypress_test_runner.ts | 14 +- x-pack/plugins/apm/ftr_e2e/tsconfig.json | 1 + .../index_lifecycle_phase_select.tsx | 1 + .../storage_details_per_service.tsx | 11 +- .../create_apm_users/create_apm_users_cli.ts | 34 +++- .../create_apm_users}/authentication.ts | 80 +------- .../create_apm_users/create_apm_users.ts | 39 ++-- .../create_apm_users/helpers/call_kibana.ts | 2 +- .../helpers/create_custom_role.ts | 60 ++++++ .../helpers/create_or_update_user.ts | 0 .../create_apm_users/helpers/get_version.ts | 2 +- .../observability/e2e/synthetics_runner.ts | 16 +- .../e2e/journeys/data_view_permissions.ts | 2 +- .../common/bootstrap_apm_synthtrace.ts | 2 +- .../test/apm_api_integration/common/config.ts | 64 ++---- .../settings/agent_keys/agent_keys.spec.ts | 2 +- 19 files changed, 370 insertions(+), 165 deletions(-) create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts rename x-pack/{test/apm_api_integration/common => plugins/apm/server/test_helpers/create_apm_users}/authentication.ts (61%) rename x-pack/plugins/apm/{scripts => server/test_helpers}/create_apm_users/create_apm_users.ts (69%) rename x-pack/plugins/apm/{scripts => server/test_helpers}/create_apm_users/helpers/call_kibana.ts (97%) create mode 100644 x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_custom_role.ts rename x-pack/plugins/apm/{scripts => server/test_helpers}/create_apm_users/helpers/create_or_update_user.ts (100%) rename x-pack/plugins/apm/{scripts => server/test_helpers}/create_apm_users/helpers/get_version.ts (96%) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts new file mode 100644 index 000000000000..e989ea5cf0fa --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts @@ -0,0 +1,192 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import moment from 'moment'; +import url from 'url'; +import { synthtrace } from '../../../../synthtrace'; +import { opbeans } from '../../../fixtures/synthtrace/opbeans'; +import { checkA11y } from '../../../support/commands'; + +const timeRange = { + rangeFrom: '2021-10-10T00:00:00.000Z', + rangeTo: '2021-10-10T00:15:00.000Z', +}; + +const storageExplorerHref = url.format({ + pathname: '/app/apm/storage-explorer', + query: timeRange, +}); + +const mainApiRequestsToIntercept = [ + { + endpoint: '/internal/apm/storage_chart', + aliasName: 'storageChartRequest', + }, + { + endpoint: '/internal/apm/storage_explorer_summary_stats', + aliasName: 'summaryStatsRequest', + }, + { + endpoint: '/internal/apm/storage_explorer', + aliasName: 'storageExlorerRequest', + }, +]; + +const mainAliasNames = mainApiRequestsToIntercept.map( + ({ aliasName }) => `@${aliasName}` +); + +describe('Storage Explorer', () => { + before(() => { + const { rangeFrom, rangeTo } = timeRange; + synthtrace.index( + opbeans({ + from: new Date(rangeFrom).getTime(), + to: new Date(rangeTo).getTime(), + }) + ); + }); + + after(() => { + synthtrace.clean(); + }); + + describe('When navigating to storage explorer without the required permissions', () => { + beforeEach(() => { + cy.loginAsViewerUser(); + cy.visitKibana(storageExplorerHref); + }); + + it('displays a prompt for permissions', () => { + cy.contains('You need permission'); + }); + }); + + describe('When navigating to storage explorer with the required permissions', () => { + beforeEach(() => { + cy.loginAsMonitorUser(); + cy.visitKibana(storageExplorerHref); + }); + + it('has no detectable a11y violations on load', () => { + cy.contains('h1', 'Storage explorer'); + // set skipFailures to true to not fail the test when there are accessibility failures + checkA11y({ skipFailures: true }); + }); + + it('has a list of summary stats', () => { + cy.contains('Total APM size'); + cy.contains('Daily data generation'); + cy.contains('Traces per minute'); + cy.contains('Number of services'); + }); + + it('renders the storage timeseries chart', () => { + cy.get('[data-test-subj="storageExplorerTimeseriesChart"]'); + }); + + it('has a list of services and environments', () => { + cy.contains('opbeans-node'); + cy.contains('opbeans-java'); + cy.contains('opbeans-rum'); + cy.get('td:contains(production)').should('have.length', 3); + }); + + it('when clicking on a service it loads the service overview for that service', () => { + cy.contains('opbeans-node').click({ force: true }); + cy.url().should('include', '/apm/services/opbeans-node/overview'); + cy.contains('h1', 'opbeans-node'); + }); + }); + + describe('Calls APIs', () => { + beforeEach(() => { + mainApiRequestsToIntercept.forEach(({ endpoint, aliasName }) => { + cy.intercept({ pathname: endpoint }).as(aliasName); + }); + + cy.loginAsMonitorUser(); + cy.visitKibana(storageExplorerHref); + }); + + it('with the correct environment when changing the environment', () => { + cy.wait(mainAliasNames); + + cy.get('[data-test-subj="environmentFilter"]').type('production'); + + cy.contains('button', 'production').click({ force: true }); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: mainAliasNames, + value: 'environment=production', + }); + }); + + it('when clicking the refresh button', () => { + cy.wait(mainAliasNames); + cy.contains('Refresh').click(); + cy.wait(mainAliasNames); + }); + + it('when selecting a different time range and clicking the update button', () => { + cy.wait(mainAliasNames); + + cy.selectAbsoluteTimeRange( + moment(timeRange.rangeFrom).subtract(5, 'm').toISOString(), + moment(timeRange.rangeTo).subtract(5, 'm').toISOString() + ); + cy.contains('Update').click(); + cy.wait(mainAliasNames); + + cy.contains('Refresh').click(); + cy.wait(mainAliasNames); + }); + + it('with the correct lifecycle phase when changing the lifecycle phase', () => { + cy.wait(mainAliasNames); + + cy.get('[data-test-subj="storageExplorerLifecyclePhaseSelect"]').click(); + cy.contains('button', 'Warm').click(); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: mainAliasNames, + value: 'indexLifecyclePhase=warm', + }); + }); + }); + + describe('Storage details per service', () => { + beforeEach(() => { + const apiRequestsToIntercept = [ + ...mainApiRequestsToIntercept, + { + endpoint: '/internal/apm/services/opbeans-node/storage_details', + aliasName: 'storageDetailsRequest', + }, + ]; + + apiRequestsToIntercept.forEach(({ endpoint, aliasName }) => { + cy.intercept({ pathname: endpoint }).as(aliasName); + }); + + cy.loginAsMonitorUser(); + cy.visitKibana(storageExplorerHref); + }); + + it('shows storage details', () => { + cy.wait(mainAliasNames); + cy.contains('opbeans-node'); + + cy.get('[data-test-subj="storageDetailsButton_opbeans-node"]').click(); + cy.get('[data-test-subj="loadingSpinner"]').should('be.visible'); + cy.wait('@storageDetailsRequest'); + + cy.contains('Service storage details'); + cy.get('[data-test-subj="storageExplorerTimeseriesChart"]'); + cy.get('[data-test-subj="serviceStorageDetailsTable"]'); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 692926d3049c..7830e791c365 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -9,13 +9,21 @@ import { Interception } from 'cypress/types/net-stubbing'; import 'cypress-axe'; import moment from 'moment'; import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/axe-config'; +import { ApmUsername } from '../../../server/test_helpers/create_apm_users/authentication'; Cypress.Commands.add('loginAsViewerUser', () => { - return cy.loginAs({ username: 'viewer', password: 'changeme' }); + return cy.loginAs({ username: ApmUsername.viewerUser, password: 'changeme' }); }); Cypress.Commands.add('loginAsEditorUser', () => { - return cy.loginAs({ username: 'editor', password: 'changeme' }); + return cy.loginAs({ username: ApmUsername.editorUser, password: 'changeme' }); +}); + +Cypress.Commands.add('loginAsMonitorUser', () => { + return cy.loginAs({ + username: ApmUsername.apmMonitorIndices, + password: 'changeme', + }); }); Cypress.Commands.add( diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts index 27720210b668..2235847e584a 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts @@ -9,6 +9,7 @@ declare namespace Cypress { interface Chainable { loginAsViewerUser(): Cypress.Chainable>; loginAsEditorUser(): Cypress.Chainable>; + loginAsMonitorUser(): Cypress.Chainable>; loginAs(params: { username: string; password: string; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress_test_runner.ts b/x-pack/plugins/apm/ftr_e2e/cypress_test_runner.ts index 8af466b87262..eba57b28ec0b 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress_test_runner.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress_test_runner.ts @@ -11,7 +11,7 @@ import { esTestConfig } from '@kbn/test'; import { apm, createLogger, LogLevel } from '@kbn/apm-synthtrace'; import path from 'path'; import { FtrProviderContext } from './ftr_provider_context'; -import { createApmUsers } from '../scripts/create_apm_users/create_apm_users'; +import { createApmUsers } from '../server/test_helpers/create_apm_users/create_apm_users'; export async function cypressTestRunner({ getService }: FtrProviderContext) { const config = getService('config'); @@ -26,12 +26,6 @@ export async function cypressTestRunner({ getService }: FtrProviderContext) { const username = config.get('servers.elasticsearch.username'); const password = config.get('servers.elasticsearch.password'); - // Creates APM users - await createApmUsers({ - elasticsearch: { username, password }, - kibana: { hostname: kibanaUrl }, - }); - const esNode = Url.format({ protocol: config.get('servers.elasticsearch.protocol'), port: config.get('servers.elasticsearch.port'), @@ -39,6 +33,12 @@ export async function cypressTestRunner({ getService }: FtrProviderContext) { auth: `${username}:${password}`, }); + // Creates APM users + await createApmUsers({ + elasticsearch: { node: esNode, username, password }, + kibana: { hostname: kibanaUrl }, + }); + const esRequestTimeout = config.get('timeouts.esRequestTimeout'); const kibanaClient = new apm.ApmSynthtraceKibanaClient( createLogger(LogLevel.info) diff --git a/x-pack/plugins/apm/ftr_e2e/tsconfig.json b/x-pack/plugins/apm/ftr_e2e/tsconfig.json index 84a66afe4588..730971a284ed 100644 --- a/x-pack/plugins/apm/ftr_e2e/tsconfig.json +++ b/x-pack/plugins/apm/ftr_e2e/tsconfig.json @@ -18,5 +18,6 @@ "references": [ { "path": "../../../test/tsconfig.json" }, { "path": "../../../../test/tsconfig.json" }, + { "path": "../tsconfig.json" }, ] } diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx index 124579e0562e..10554b4ff827 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index_lifecycle_phase_select.tsx @@ -130,6 +130,7 @@ export function IndexLifecyclePhaseSelect() { }} hasDividers style={{ minWidth: 200 }} + data-test-subj="storageExplorerLifecyclePhaseSelect" /> ); } diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx index 3d9cd14c8e95..0a101d335768 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx @@ -186,7 +186,10 @@ export function StorageDetailsPerService({ - + - + {processorEventStats.map( ({ processorEventLabel, docs, size }) => ( <> diff --git a/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users_cli.ts b/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users_cli.ts index 194639128b4c..24cda99fd669 100644 --- a/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users_cli.ts +++ b/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users_cli.ts @@ -8,13 +8,17 @@ /* eslint-disable no-console */ import { argv } from 'yargs'; -import { AbortError, isAxiosError } from './helpers/call_kibana'; -import { createApmUsers } from './create_apm_users'; -import { getKibanaVersion } from './helpers/get_version'; +import { + AbortError, + isAxiosError, +} from '../../server/test_helpers/create_apm_users/helpers/call_kibana'; +import { createApmUsers } from '../../server/test_helpers/create_apm_users/create_apm_users'; +import { getKibanaVersion } from '../../server/test_helpers/create_apm_users/helpers/get_version'; async function init() { const esUserName = (argv.username as string) || 'elastic'; const esPassword = argv.password as string | undefined; + const esUrl = argv.esUrl as string | undefined; const kibanaBaseUrl = argv.kibanaUrl as string | undefined; if (!esPassword) { @@ -24,6 +28,20 @@ async function init() { process.exit(); } + if (!esUrl) { + console.error( + 'Please specify the url for elasticsearch: `--es-url http://localhost:9200` ' + ); + process.exit(); + } + + if (!esUrl.startsWith('https://') && !esUrl.startsWith('http://')) { + console.error( + 'Elasticsearch url must be prefixed with http(s):// `--es-url http://localhost:9200`' + ); + process.exit(); + } + if (!kibanaBaseUrl) { console.error( 'Please specify the url for Kibana: `--kibana-url http://localhost:5601` ' @@ -42,7 +60,11 @@ async function init() { } const kibana = { hostname: kibanaBaseUrl }; - const elasticsearch = { username: esUserName, password: esPassword }; + const elasticsearch = { + node: esUrl, + username: esUserName, + password: esPassword, + }; console.log({ kibana, elasticsearch }); @@ -50,9 +72,7 @@ async function init() { console.log(`Connected to Kibana ${version}`); const users = await createApmUsers({ elasticsearch, kibana }); - const credentials = users - .map((u) => ` - ${u.username} / ${esPassword}`) - .join('\n'); + const credentials = users.map((u) => ` - ${u} / ${esPassword}`).join('\n'); console.log( `\nYou can now login to ${kibana.hostname} with:\n${credentials}` diff --git a/x-pack/test/apm_api_integration/common/authentication.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts similarity index 61% rename from x-pack/test/apm_api_integration/common/authentication.ts rename to x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts index 93bae236d5dc..f3d15ee6db21 100644 --- a/x-pack/test/apm_api_integration/common/authentication.ts +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts @@ -5,15 +5,7 @@ * 2.0. */ -import { Client } from '@elastic/elasticsearch'; -import { PrivilegeType } from '@kbn/apm-plugin/common/privilege_type'; -import { ToolingLog } from '@kbn/tooling-log'; -import { omit } from 'lodash'; -import { KbnClientRequesterError } from '@kbn/test'; -import { AxiosError } from 'axios'; -import { SecurityServiceProvider } from '../../../../test/common/services/security'; - -type SecurityService = Awaited>; +import { PrivilegeType } from '../../../common/privilege_type'; export enum ApmUsername { noAccessUser = 'no_access_user', @@ -34,7 +26,7 @@ export enum ApmCustomRolename { apmMonitorIndices = 'apm_monitor_indices', } -const customRoles = { +export const customRoles = { [ApmCustomRolename.apmReadUserWithoutMlAccess]: { elasticsearch: { cluster: [], @@ -97,7 +89,7 @@ const customRoles = { }, }; -const users: Record< +export const users: Record< ApmUsername, { builtInRoleNames?: string[]; customRoleNames?: ApmCustomRolename[] } > = { @@ -132,70 +124,4 @@ const users: Record< }, }; -function logErrorResponse(logger: ToolingLog, e: Error) { - if (e instanceof KbnClientRequesterError) { - logger.error(`KbnClientRequesterError: ${JSON.stringify(e.axiosError?.response?.data)}`); - } else if (e instanceof AxiosError) { - logger.error(`AxiosError: ${JSON.stringify(e.response?.data)}`); - } else { - logger.error(`Unknown error: ${e.constructor.name}`); - } -} - -export async function createApmUser({ - username, - security, - es, - logger, -}: { - username: ApmUsername; - security: SecurityService; - es: Client; - logger: ToolingLog; -}) { - const user = users[username]; - - if (!user) { - throw new Error(`No configuration found for ${username}`); - } - - const { builtInRoleNames = [], customRoleNames = [] } = user; - - try { - // create custom roles - await Promise.all( - customRoleNames.map(async (roleName) => createCustomRole({ roleName, security, es })) - ); - - // create user - await security.user.create(username, { - full_name: username, - password: APM_TEST_PASSWORD, - roles: [...builtInRoleNames, ...customRoleNames], - }); - } catch (e) { - logErrorResponse(logger, e); - throw e; - } -} - -async function createCustomRole({ - roleName, - security, - es, -}: { - roleName: ApmCustomRolename; - security: SecurityService; - es: Client; -}) { - const role = customRoles[roleName]; - - // Add application privileges with es client as they are not supported by - // security.user.create. They are preserved when updating the role below - if ('applications' in role) { - await es.security.putRole({ name: roleName, body: role }); - } - await security.role.create(roleName, omit(role, 'applications')); -} - export const APM_TEST_PASSWORD = 'changeme'; diff --git a/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/create_apm_users.ts similarity index 69% rename from x-pack/plugins/apm/scripts/create_apm_users/create_apm_users.ts rename to x-pack/plugins/apm/server/test_helpers/create_apm_users/create_apm_users.ts index 7532392c9a8b..8e9e39373752 100644 --- a/x-pack/plugins/apm/scripts/create_apm_users/create_apm_users.ts +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/create_apm_users.ts @@ -5,10 +5,13 @@ * 2.0. */ +import { asyncForEach } from '@kbn/std'; import { AbortError, callKibana } from './helpers/call_kibana'; import { createOrUpdateUser } from './helpers/create_or_update_user'; - +import { ApmUsername, users } from './authentication'; +import { createCustomRole } from './helpers/create_custom_role'; export interface Elasticsearch { + node: string; username: string; password: string; } @@ -28,6 +31,7 @@ export async function createApmUsers({ elasticsearch, kibana, }); + if (!isCredentialsValid) { throw new AbortError('Invalid username/password'); } @@ -36,24 +40,33 @@ export async function createApmUsers({ elasticsearch, kibana, }); + if (!isSecurityEnabled) { throw new AbortError('Security must be enabled!'); } - // user definitions - const users = [ - { username: 'viewer', roles: ['viewer'] }, - { username: 'editor', roles: ['editor'] }, - ]; + const apmUsers = Object.values(ApmUsername); + await asyncForEach(apmUsers, async (username) => { + const user = users[username]; + const { builtInRoleNames = [], customRoleNames = [] } = user; + + // create custom roles + await Promise.all( + customRoleNames.map(async (roleName) => + createCustomRole({ elasticsearch, kibana, roleName }) + ) + ); - // create users - await Promise.all( - users.map(async (user) => - createOrUpdateUser({ elasticsearch, kibana, user }) - ) - ); + // create user + const roles = builtInRoleNames.concat(customRoleNames); + await createOrUpdateUser({ + elasticsearch, + kibana, + user: { username, roles }, + }); + }); - return users; + return apmUsers; } async function getIsSecurityEnabled({ diff --git a/x-pack/plugins/apm/scripts/create_apm_users/helpers/call_kibana.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/call_kibana.ts similarity index 97% rename from x-pack/plugins/apm/scripts/create_apm_users/helpers/call_kibana.ts rename to x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/call_kibana.ts index 1e6bd2e02c41..72312645a644 100644 --- a/x-pack/plugins/apm/scripts/create_apm_users/helpers/call_kibana.ts +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/call_kibana.ts @@ -13,7 +13,7 @@ export async function callKibana({ kibana, options, }: { - elasticsearch: Elasticsearch; + elasticsearch: Omit; kibana: Kibana; options: AxiosRequestConfig; }): Promise { diff --git a/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_custom_role.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_custom_role.ts new file mode 100644 index 000000000000..ac906fcbfb5e --- /dev/null +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_custom_role.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Client } from '@elastic/elasticsearch'; +import { omit } from 'lodash'; +import { Elasticsearch, Kibana } from '../create_apm_users'; +import { callKibana } from './call_kibana'; +import { customRoles, ApmCustomRolename } from '../authentication'; + +export async function createCustomRole({ + elasticsearch, + kibana, + roleName, +}: { + elasticsearch: Elasticsearch; + kibana: Kibana; + roleName: ApmCustomRolename; +}) { + const role = customRoles[roleName]; + + // Add application privileges with es client as they are not supported by + // the security API. They are preserved when updating the role below + if ('applications' in role) { + const esClient = getEsClient(elasticsearch); + await esClient.security.putRole({ name: roleName, body: role }); + } + + await callKibana({ + elasticsearch, + kibana, + options: { + method: 'PUT', + url: `/api/security/role/${roleName}`, + data: { + ...omit(role, 'applications'), + }, + }, + }); +} + +export function getEsClient(elasticsearch: Elasticsearch) { + const { node, username, password } = elasticsearch; + const client = new Client({ + node, + tls: { + rejectUnauthorized: false, + }, + requestTimeout: 120000, + auth: { + username, + password, + }, + }); + + return client; +} diff --git a/x-pack/plugins/apm/scripts/create_apm_users/helpers/create_or_update_user.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_or_update_user.ts similarity index 100% rename from x-pack/plugins/apm/scripts/create_apm_users/helpers/create_or_update_user.ts rename to x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/create_or_update_user.ts diff --git a/x-pack/plugins/apm/scripts/create_apm_users/helpers/get_version.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/get_version.ts similarity index 96% rename from x-pack/plugins/apm/scripts/create_apm_users/helpers/get_version.ts rename to x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/get_version.ts index a809efe7402e..a00747ae7858 100644 --- a/x-pack/plugins/apm/scripts/create_apm_users/helpers/get_version.ts +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/helpers/get_version.ts @@ -13,7 +13,7 @@ export async function getKibanaVersion({ elasticsearch, kibana, }: { - elasticsearch: Elasticsearch; + elasticsearch: Omit; kibana: Kibana; }) { try { diff --git a/x-pack/plugins/observability/e2e/synthetics_runner.ts b/x-pack/plugins/observability/e2e/synthetics_runner.ts index 25c236c6da58..a24771c091c0 100644 --- a/x-pack/plugins/observability/e2e/synthetics_runner.ts +++ b/x-pack/plugins/observability/e2e/synthetics_runner.ts @@ -10,7 +10,7 @@ import Url from 'url'; import { run as syntheticsRun } from '@elastic/synthetics'; import { PromiseType } from 'utility-types'; -import { createApmUsers } from '@kbn/apm-plugin/scripts/create_apm_users/create_apm_users'; +import { createApmUsers } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/create_apm_users'; import { esArchiverUnload } from './tasks/es_archiver'; @@ -23,6 +23,7 @@ export interface ArgParams { export class SyntheticsRunner { public getService: any; public kibanaUrl: string; + private elasticsearchUrl: string; public testFilesLoaded: boolean = false; @@ -31,6 +32,7 @@ export class SyntheticsRunner { constructor(getService: any, params: ArgParams) { this.getService = getService; this.kibanaUrl = this.getKibanaUrl(); + this.elasticsearchUrl = this.getElasticsearchUrl(); this.params = params; } @@ -40,7 +42,7 @@ export class SyntheticsRunner { async createTestUsers() { await createApmUsers({ - elasticsearch: { username: 'elastic', password: 'changeme' }, + elasticsearch: { node: this.elasticsearchUrl, username: 'elastic', password: 'changeme' }, kibana: { hostname: this.kibanaUrl }, }); } @@ -79,6 +81,16 @@ export class SyntheticsRunner { }); } + getElasticsearchUrl() { + const config = this.getService('config'); + + return Url.format({ + protocol: config.get('servers.elasticsearch.protocol'), + hostname: config.get('servers.elasticsearch.hostname'), + port: config.get('servers.elasticsearch.port'), + }); + } + async run() { if (!this.testFilesLoaded) { throw new Error('Test files not loaded'); diff --git a/x-pack/plugins/synthetics/e2e/journeys/data_view_permissions.ts b/x-pack/plugins/synthetics/e2e/journeys/data_view_permissions.ts index aa29b92b7ea2..e5714234de69 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/data_view_permissions.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/data_view_permissions.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { callKibana } from '@kbn/apm-plugin/scripts/create_apm_users/helpers/call_kibana'; +import { callKibana } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/helpers/call_kibana'; import { byTestId, waitForLoadingToFinish } from '@kbn/observability-plugin/e2e/utils'; import { loginPageProvider } from '../page_objects/login'; diff --git a/x-pack/test/apm_api_integration/common/bootstrap_apm_synthtrace.ts b/x-pack/test/apm_api_integration/common/bootstrap_apm_synthtrace.ts index 96d0cbf3f8e7..0a790d261ef4 100644 --- a/x-pack/test/apm_api_integration/common/bootstrap_apm_synthtrace.ts +++ b/x-pack/test/apm_api_integration/common/bootstrap_apm_synthtrace.ts @@ -7,7 +7,7 @@ import { apm, createLogger, LogLevel } from '@kbn/apm-synthtrace'; import { esTestConfig } from '@kbn/test'; -import { APM_TEST_PASSWORD } from './authentication'; +import { APM_TEST_PASSWORD } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/authentication'; import { InheritedFtrProviderContext } from './ftr_provider_context'; export async function bootstrapApmSynthtrace( diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index e01c5fbf4054..431b445d3a1a 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -8,11 +8,12 @@ import { FtrConfigProviderContext } from '@kbn/test'; import supertest from 'supertest'; import { format, UrlObject } from 'url'; -import { Client } from '@elastic/elasticsearch'; -import { ToolingLog } from '@kbn/tooling-log'; -import { SecurityServiceProvider } from '../../../../test/common/services/security'; +import { + ApmUsername, + APM_TEST_PASSWORD, +} from '@kbn/apm-plugin/server/test_helpers/create_apm_users/authentication'; +import { createApmUsers } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/create_apm_users'; import { InheritedFtrProviderContext, InheritedServices } from './ftr_provider_context'; -import { createApmUser, APM_TEST_PASSWORD, ApmUsername } from './authentication'; import { APMFtrConfigName } from '../configs'; import { createApmApiClient } from './apm_api_supertest'; import { RegistryProvider } from './registry'; @@ -25,17 +26,8 @@ export interface ApmFtrConfig { kibanaConfig?: Record; } -type SecurityService = Awaited>; - function getLegacySupertestClient(kibanaServer: UrlObject, username: ApmUsername) { return async (context: InheritedFtrProviderContext) => { - const security = context.getService('security'); - const es = context.getService('es'); - const logger = context.getService('log'); - await security.init(); - - await createApmUser({ security, username, es, logger }); - const url = format({ ...kibanaServer, auth: `${username}:${APM_TEST_PASSWORD}`, @@ -47,19 +39,11 @@ function getLegacySupertestClient(kibanaServer: UrlObject, username: ApmUsername async function getApmApiClient({ kibanaServer, - security, username, - es, - logger, }: { kibanaServer: UrlObject; - security: SecurityService; username: ApmUsername; - es: Client; - logger: ToolingLog; }) { - await createApmUser({ security, username, es, logger }); - const url = format({ ...kibanaServer, auth: `${username}:${APM_TEST_PASSWORD}`, @@ -81,6 +65,8 @@ export function createTestConfig(config: ApmFtrConfig) { const services = xPackAPITestsConfig.get('services') as InheritedServices; const servers = xPackAPITestsConfig.get('servers'); const kibanaServer = servers.kibana as UrlObject; + const kibanaServerUrl = format(kibanaServer); + const esServer = servers.elasticsearch as UrlObject; return { testFiles: [require.resolve('../tests')], @@ -91,72 +77,50 @@ export function createTestConfig(config: ApmFtrConfig) { apmFtrConfig: () => config, registry: RegistryProvider, synthtraceEsClient: (context: InheritedFtrProviderContext) => { - const kibanaServerUrl = format(kibanaServer); return bootstrapApmSynthtrace(context, kibanaServerUrl); }, apmApiClient: async (context: InheritedFtrProviderContext) => { - const security = context.getService('security'); - const es = context.getService('es'); - const logger = context.getService('log'); + const { username, password } = servers.kibana; + const esUrl = format(esServer); - await security.init(); + // Creates APM users + await createApmUsers({ + elasticsearch: { node: esUrl, username, password }, + kibana: { hostname: kibanaServerUrl }, + }); return { noAccessUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.noAccessUser, - es, - logger, }), readUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.viewerUser, - es, - logger, }), writeUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.editorUser, - es, - logger, }), annotationWriterUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.apmAnnotationsWriteUser, - es, - logger, }), noMlAccessUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.apmReadUserWithoutMlAccess, - es, - logger, }), manageOwnAgentKeysUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.apmManageOwnAgentKeys, - es, - logger, }), createAndAllAgentKeysUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.apmManageOwnAndCreateAgentKeys, - es, - logger, }), monitorIndicesUser: await getApmApiClient({ kibanaServer, - security, username: ApmUsername.apmMonitorIndices, - es, - logger, }), }; }, diff --git a/x-pack/test/apm_api_integration/tests/settings/agent_keys/agent_keys.spec.ts b/x-pack/test/apm_api_integration/tests/settings/agent_keys/agent_keys.spec.ts index c0d6500fd298..19459d2a4ec1 100644 --- a/x-pack/test/apm_api_integration/tests/settings/agent_keys/agent_keys.spec.ts +++ b/x-pack/test/apm_api_integration/tests/settings/agent_keys/agent_keys.spec.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { first } from 'lodash'; import { PrivilegeType } from '@kbn/apm-plugin/common/privilege_type'; +import { ApmUsername } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/authentication'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { ApmApiError, ApmApiSupertest } from '../../../common/apm_api_supertest'; -import { ApmUsername } from '../../../common/authentication'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); From 2b4f5d9a640212a85675554c87be36e74e508b83 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+dimaanj@users.noreply.github.com> Date: Thu, 29 Sep 2022 11:19:58 +0300 Subject: [PATCH 098/185] [Graph] Align Inspect requests experience with common approach (#141222) * [Graph] hide inspect button when there is no relevant request content * [Graph] fix functional * [Graph] add common inspector * [Discover] fix checks * [Discover] apply suggestion --- x-pack/plugins/graph/kibana.json | 3 +- x-pack/plugins/graph/public/application.tsx | 2 + .../graph/public/apps/workspace_route.tsx | 14 ++- .../graph/public/components/inspect_panel.tsx | 98 ------------------- .../workspace_layout.test.tsx | 9 ++ .../workspace_layout/workspace_layout.tsx | 16 ++- .../workspace_top_nav_menu.tsx | 22 ++++- .../graph/public/helpers/use_graph_loader.ts | 86 ++++++++++++---- .../graph/public/helpers/use_inspector.ts | 38 +++++++ x-pack/plugins/graph/public/plugin.ts | 3 + .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - x-pack/test/accessibility/apps/graph.ts | 6 -- 14 files changed, 156 insertions(+), 150 deletions(-) delete mode 100644 x-pack/plugins/graph/public/components/inspect_panel.tsx create mode 100644 x-pack/plugins/graph/public/helpers/use_inspector.ts diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 641e595893c0..6db4ca194d7d 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -9,7 +9,8 @@ "data", "navigation", "savedObjects", - "unifiedSearch" + "unifiedSearch", + "inspector" ], "optionalPlugins": [ "home", diff --git a/x-pack/plugins/graph/public/application.tsx b/x-pack/plugins/graph/public/application.tsx index 1976b12621f4..bbb14a96ac5e 100644 --- a/x-pack/plugins/graph/public/application.tsx +++ b/x-pack/plugins/graph/public/application.tsx @@ -27,6 +27,7 @@ import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; +import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public'; import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; import './index.scss'; @@ -70,6 +71,7 @@ export interface GraphDependencies { uiSettings: IUiSettingsClient; history: ScopedHistory; spaces?: SpacesApi; + inspect: InspectorPublicPluginStart; } export type GraphServices = Omit; diff --git a/x-pack/plugins/graph/public/apps/workspace_route.tsx b/x-pack/plugins/graph/public/apps/workspace_route.tsx index 051c0fa66ab8..9b1fde2e7cb2 100644 --- a/x-pack/plugins/graph/public/apps/workspace_route.tsx +++ b/x-pack/plugins/graph/public/apps/workspace_route.tsx @@ -43,6 +43,7 @@ export const WorkspaceRoute = ({ setHeaderActionMenu, spaces, indexPatterns: getIndexPatternProvider, + inspect, }, }: WorkspaceRouteProps) => { /** @@ -64,11 +65,6 @@ export const WorkspaceRoute = ({ [getIndexPatternProvider.get] ); - const { loading, callNodeProxy, callSearchNodeProxy, handleSearchQueryError } = useGraphLoader({ - toastNotifications, - coreStart, - }); - const services = useMemo( () => ({ appName: 'graph', @@ -80,6 +76,12 @@ export const WorkspaceRoute = ({ [coreStart, data, storage, unifiedSearch] ); + const { loading, requestAdapter, callNodeProxy, callSearchNodeProxy, handleSearchQueryError } = + useGraphLoader({ + toastNotifications, + coreStart, + }); + const [store] = useState(() => createGraphStore({ basePath: getBasePath(), @@ -150,6 +152,8 @@ export const WorkspaceRoute = ({ overlays={overlays} savedWorkspace={savedWorkspace} indexPatternProvider={indexPatternProvider} + inspect={inspect} + requestAdapter={requestAdapter} /> diff --git a/x-pack/plugins/graph/public/components/inspect_panel.tsx b/x-pack/plugins/graph/public/components/inspect_panel.tsx deleted file mode 100644 index e6197bac16ba..000000000000 --- a/x-pack/plugins/graph/public/components/inspect_panel.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useMemo, useState } from 'react'; -import { EuiTab, EuiTabs, EuiText } from '@elastic/eui'; -import { monaco, XJsonLang } from '@kbn/monaco'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { CodeEditor } from '@kbn/kibana-react-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; - -interface InspectPanelProps { - showInspect: boolean; - indexPattern?: DataView; - lastRequest?: string; - lastResponse?: string; -} - -const CODE_EDITOR_OPTIONS: monaco.editor.IStandaloneEditorConstructionOptions = { - automaticLayout: true, - fontSize: 12, - lineNumbers: 'on', - minimap: { - enabled: false, - }, - overviewRulerBorder: false, - readOnly: true, - scrollbar: { - alwaysConsumeMouseWheel: false, - }, - scrollBeyondLastLine: false, - wordWrap: 'on', - wrappingIndent: 'indent', -}; - -const dummyCallback = () => {}; - -export const InspectPanel = ({ - showInspect, - lastRequest, - lastResponse, - indexPattern, -}: InspectPanelProps) => { - const [selectedTabId, setSelectedTabId] = useState('request'); - - const onRequestClick = () => setSelectedTabId('request'); - const onResponseClick = () => setSelectedTabId('response'); - - const editorContent = useMemo( - () => (selectedTabId === 'request' ? lastRequest : lastResponse), - [lastRequest, lastResponse, selectedTabId] - ); - - if (showInspect) { - return ( -
    -
    -
    - -
    - -
    - - http://host:port/{indexPattern?.id}/_graph/explore - - - - - - - - - - -
    -
    -
    - ); - } - - return null; -}; diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx index cb43b5ec5d68..72cd35997b87 100644 --- a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx @@ -10,6 +10,7 @@ import { shallow } from 'enzyme'; import { WorkspaceLayoutComponent } from '.'; import { coreMock } from '@kbn/core/public/mocks'; import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; +import { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public'; import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; import { GraphSavePolicy, @@ -51,6 +52,14 @@ describe('workspace_layout', () => { aliasTargetId: '', } as SharingSavedObjectProps, spaces: spacesPluginMock.createStartContract(), + inspect: { open: jest.fn() } as unknown as InspectorStart, + requestAdapter: { + start: () => ({ + stats: jest.fn(), + json: jest.fn(), + }), + reset: jest.fn(), + } as unknown as RequestAdapter, workspace: {} as unknown as Workspace, }; it('should display conflict notification if outcome is conflict', () => { diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx index 3eede479bd80..5de95636a61d 100644 --- a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx @@ -11,6 +11,7 @@ import { EuiSpacer } from '@elastic/eui'; import { connect } from 'react-redux'; import { useLocation } from 'react-router-dom'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { SearchBar } from '../search_bar'; import { GraphState, @@ -20,7 +21,6 @@ import { import { FieldManager } from '../field_manager'; import { ControlType, IndexPatternProvider, TermIntersect, WorkspaceNode } from '../../types'; import { WorkspaceTopNavMenu } from './workspace_top_nav_menu'; -import { InspectPanel } from '../inspect_panel'; import { GuidancePanel } from '../guidance_panel'; import { GraphTitle } from '../graph_title'; import { GraphWorkspaceSavedObject, Workspace } from '../../types'; @@ -49,6 +49,7 @@ type WorkspaceLayoutProps = Pick< | 'canEditDrillDownUrls' | 'overlays' | 'spaces' + | 'inspect' > & { renderCounter: number; workspace?: Workspace; @@ -56,6 +57,7 @@ type WorkspaceLayoutProps = Pick< savedWorkspace: GraphWorkspaceSavedObject; indexPatternProvider: IndexPatternProvider; sharingSavedObjectProps?: SharingSavedObjectProps; + requestAdapter: RequestAdapter; }; interface WorkspaceLayoutStateProps { @@ -80,9 +82,10 @@ export const WorkspaceLayoutComponent = ({ setHeaderActionMenu, sharingSavedObjectProps, spaces, + inspect, + requestAdapter, }: WorkspaceLayoutProps & WorkspaceLayoutStateProps) => { const [currentIndexPattern, setCurrentIndexPattern] = useState(); - const [showInspect, setShowInspect] = useState(false); const [pickerOpen, setPickerOpen] = useState(false); const [mergeCandidates, setMergeCandidates] = useState([]); const [control, setControl] = useState('none'); @@ -188,20 +191,15 @@ export const WorkspaceLayoutComponent = ({ graphSavePolicy={graphSavePolicy} navigation={navigation} capabilities={capabilities} + inspect={inspect} + requestAdapter={requestAdapter} coreStart={coreStart} canEditDrillDownUrls={canEditDrillDownUrls} - setShowInspect={setShowInspect} confirmWipeWorkspace={confirmWipeWorkspace} setHeaderActionMenu={setHeaderActionMenu} isInitialized={isInitialized} /> - {isInitialized && }
    >; confirmWipeWorkspace: ( onConfirm: () => void, text?: string, @@ -30,9 +31,11 @@ interface WorkspaceTopNavMenuProps { graphSavePolicy: GraphSavePolicy; navigation: NavigationStart; capabilities: Capabilities; + inspect: InspectorPublicPluginStart; coreStart: CoreStart; canEditDrillDownUrls: boolean; isInitialized: boolean; + requestAdapter: RequestAdapter; } export const WorkspaceTopNavMenu = (props: WorkspaceTopNavMenuProps) => { @@ -40,6 +43,12 @@ export const WorkspaceTopNavMenu = (props: WorkspaceTopNavMenuProps) => { const location = useLocation(); const history = useHistory(); const allSavingDisabled = props.graphSavePolicy === 'none'; + const isInspectDisabled = !props.workspace?.lastRequest || !props.workspace.lastRequest; + + const { onOpenInspector } = useInspector({ + inspect: props.inspect, + requestAdapter: props.requestAdapter, + }); // ===== Menubar configuration ========= const { TopNavMenu } = props.navigation.ui; @@ -107,7 +116,7 @@ export const WorkspaceTopNavMenu = (props: WorkspaceTopNavMenuProps) => { topNavMenu.push({ key: 'inspect', disableButton() { - return props.workspace === null; + return isInspectDisabled; }, label: i18n.translate('xpack.graph.topNavMenu.inspectLabel', { defaultMessage: 'Inspect', @@ -116,7 +125,14 @@ export const WorkspaceTopNavMenu = (props: WorkspaceTopNavMenuProps) => { defaultMessage: 'Inspect', }), run: () => { - props.setShowInspect((prevShowInspect) => !prevShowInspect); + onOpenInspector(); + }, + tooltip: () => { + if (isInspectDisabled) { + return i18n.translate('xpack.graph.topNavMenu.inspectButton.disabledTooltip', { + defaultMessage: 'Perform a search or expand a node to enable Inspect', + }); + } }, testId: 'graphInspectButton', }); diff --git a/x-pack/plugins/graph/public/helpers/use_graph_loader.ts b/x-pack/plugins/graph/public/helpers/use_graph_loader.ts index 5b4f3bbbf1e4..0d50039ab979 100644 --- a/x-pack/plugins/graph/public/helpers/use_graph_loader.ts +++ b/x-pack/plugins/graph/public/helpers/use_graph_loader.ts @@ -5,10 +5,12 @@ * 2.0. */ -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import type { CoreStart, ToastsStart } from '@kbn/core/public'; import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; import { i18n } from '@kbn/i18n'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { RequestAdapter } from '@kbn/inspector-plugin/public'; import type { ExploreRequest, GraphExploreCallback, @@ -24,6 +26,7 @@ interface UseGraphLoaderProps { export const useGraphLoader = ({ toastNotifications, coreStart }: UseGraphLoaderProps) => { const [loading, setLoading] = useState(false); + const requestAdapter = useMemo(() => new RequestAdapter(), []); const handleHttpError = useCallback( (error: IHttpFetchError) => { @@ -52,21 +55,56 @@ export const useGraphLoader = ({ toastNotifications, coreStart }: UseGraphLoader [toastNotifications] ); + const getRequestInspector = useCallback( + (indexName: string) => { + const inspectRequest = requestAdapter.start( + i18n.translate('xpack.graph.inspectAdapter.graphExploreRequest.name', { + defaultMessage: 'Data', + }), + { + description: i18n.translate( + 'xpack.graph.inspectAdapter.graphExploreRequest.description', + { + defaultMessage: 'This request queries Elasticsearch to fetch the data for the Graph.', + } + ), + } + ); + inspectRequest.stats({ + indexPattern: { + label: i18n.translate( + 'xpack.graph.inspectAdapter.graphExploreRequest.dataView.description.label', + { defaultMessage: 'Data view' } + ), + value: indexName, + description: i18n.translate( + 'xpack.graph.inspectAdapter.graphExploreRequest.dataView.description', + { defaultMessage: 'The data view that connected to the Elasticsearch indices.' } + ), + }, + }); + return inspectRequest; + }, + [requestAdapter] + ); + // Replacement function for graphClientWorkspace's comms so // that it works with Kibana. const callNodeProxy = useCallback( (indexName: string, query: ExploreRequest, responseHandler: GraphExploreCallback) => { - const request = { - body: JSON.stringify({ - index: indexName, - query, - }), - }; + const dsl = { index: indexName, query }; + const request = { body: JSON.stringify(dsl) }; setLoading(true); + + requestAdapter.reset(); + const inspectRequest = getRequestInspector(indexName); + inspectRequest.json(dsl); + return coreStart.http - .post<{ resp: { timed_out: unknown } }>('../api/graph/graphExplore', request) + .post<{ resp: estypes.GraphExploreResponse }>('../api/graph/graphExplore', request) .then(function (data) { const response = data.resp; + if (response?.timed_out) { toastNotifications.addWarning( i18n.translate('xpack.graph.exploreGraph.timedOutWarningText', { @@ -74,38 +112,48 @@ export const useGraphLoader = ({ toastNotifications, coreStart }: UseGraphLoader }) ); } + inspectRequest.stats({}).ok({ json: response }); responseHandler(response); }) - .catch(handleHttpError) + .catch((e) => { + inspectRequest.error({ json: e }); + handleHttpError(e); + }) .finally(() => setLoading(false)); }, - [coreStart.http, handleHttpError, toastNotifications] + [coreStart.http, getRequestInspector, handleHttpError, requestAdapter, toastNotifications] ); // Helper function for the graphClientWorkspace to perform a query const callSearchNodeProxy = useCallback( (indexName: string, query: SearchRequest, responseHandler: GraphSearchCallback) => { - const request = { - body: JSON.stringify({ - index: indexName, - body: query, - }), - }; + const dsl = { index: indexName, body: query }; + const request = { body: JSON.stringify(dsl) }; setLoading(true); + + requestAdapter.reset(); + const inspectRequest = getRequestInspector(indexName); + inspectRequest.json(dsl); + coreStart.http - .post<{ resp: unknown }>('../api/graph/searchProxy', request) + .post<{ resp: estypes.GraphExploreResponse }>('../api/graph/searchProxy', request) .then(function (data) { const response = data.resp; + inspectRequest.stats({}).ok({ json: response }); responseHandler(response); }) - .catch(handleHttpError) + .catch((e) => { + inspectRequest.error({ json: e }); + handleHttpError(e); + }) .finally(() => setLoading(false)); }, - [coreStart.http, handleHttpError] + [coreStart.http, getRequestInspector, handleHttpError, requestAdapter] ); return { loading, + requestAdapter, callNodeProxy, callSearchNodeProxy, handleSearchQueryError, diff --git a/x-pack/plugins/graph/public/helpers/use_inspector.ts b/x-pack/plugins/graph/public/helpers/use_inspector.ts new file mode 100644 index 000000000000..d6f237e625c3 --- /dev/null +++ b/x-pack/plugins/graph/public/helpers/use_inspector.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; +import { + Start as InspectorPublicPluginStart, + InspectorSession, + RequestAdapter, +} from '@kbn/inspector-plugin/public'; + +export const useInspector = ({ + inspect, + requestAdapter, +}: { + inspect: InspectorPublicPluginStart; + requestAdapter: RequestAdapter; +}) => { + const [inspectorSession, setInspectorSession] = useState(); + + useEffect(() => { + return () => { + if (inspectorSession) { + inspectorSession.close(); + } + }; + }, [inspectorSession]); + + const onOpenInspector = useCallback(() => { + const session = inspect.open({ requests: requestAdapter }, {}); + setInspectorSession(session); + }, [inspect, requestAdapter]); + + return { onOpenInspector }; +}; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index d4f8471426cc..96dac017eeab 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -19,6 +19,7 @@ import { DEFAULT_APP_CATEGORIES, } from '@kbn/core/public'; +import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -40,6 +41,7 @@ export interface GraphPluginStartDependencies { data: DataPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart; savedObjects: SavedObjectsStart; + inspector: InspectorPublicPluginStart; home?: HomePublicPluginStart; spaces?: SpacesApi; } @@ -110,6 +112,7 @@ export class GraphPlugin savedObjects: pluginsStart.savedObjects, uiSettings: core.uiSettings, spaces: pluginsStart.spaces, + inspect: pluginsStart.inspector, }); }, }); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index e4db41419cb9..cdaa18f14c6e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -13917,9 +13917,6 @@ "xpack.graph.icon.tachometer": "Tachymètre", "xpack.graph.icon.user": "Utilisateur", "xpack.graph.icon.users": "Utilisateurs", - "xpack.graph.inspect.requestTabTitle": "Requête", - "xpack.graph.inspect.responseTabTitle": "Réponse", - "xpack.graph.inspect.title": "Inspecter", "xpack.graph.leaveWorkspace.confirmButtonLabel": "Quitter", "xpack.graph.leaveWorkspace.confirmText": "Si vous quittez maintenant, vous perdez les modifications non enregistrées.", "xpack.graph.leaveWorkspace.modalTitle": "Modifications non enregistrées", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 557f4014f1e4..a7d49509c775 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13903,9 +13903,6 @@ "xpack.graph.icon.tachometer": "タコメーター", "xpack.graph.icon.user": "ユーザー", "xpack.graph.icon.users": "ユーザー", - "xpack.graph.inspect.requestTabTitle": "リクエスト", - "xpack.graph.inspect.responseTabTitle": "応答", - "xpack.graph.inspect.title": "検査", "xpack.graph.leaveWorkspace.confirmButtonLabel": "それでも移動", "xpack.graph.leaveWorkspace.confirmText": "今移動すると、保存されていない変更が失われます。", "xpack.graph.leaveWorkspace.modalTitle": "保存されていない変更", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7fb05ec21f71..25bd585b1343 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13923,9 +13923,6 @@ "xpack.graph.icon.tachometer": "转速表", "xpack.graph.icon.user": "用户", "xpack.graph.icon.users": "用户", - "xpack.graph.inspect.requestTabTitle": "请求", - "xpack.graph.inspect.responseTabTitle": "响应", - "xpack.graph.inspect.title": "检查", "xpack.graph.leaveWorkspace.confirmButtonLabel": "离开", "xpack.graph.leaveWorkspace.confirmText": "如果现在离开,将丢失未保存的更改。", "xpack.graph.leaveWorkspace.modalTitle": "未保存的更改", diff --git a/x-pack/test/accessibility/apps/graph.ts b/x-pack/test/accessibility/apps/graph.ts index d13ed5a58f87..85a77f481627 100644 --- a/x-pack/test/accessibility/apps/graph.ts +++ b/x-pack/test/accessibility/apps/graph.ts @@ -63,12 +63,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('saveCancelButton'); }); - it('Graph inspect panel', async function () { - await testSubjects.click('graphInspectButton'); - await a11y.testAppSnapshot(); - await testSubjects.click('graphInspectButton'); - }); - it('Graph settings - advanced settings tab', async function () { await testSubjects.click('graphSettingsButton'); await a11y.testAppSnapshot(); From a3414f6e2ff705699c3b09497fecc4d9ae049e37 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Thu, 29 Sep 2022 04:39:22 -0400 Subject: [PATCH 099/185] [Fleet] Sample README implementation for Language Clients (#141935) * wip * Adjust language clients APIs * Comment out sample card * Fix checks * Fix translations * Add breadcrumbs and expand sample page Co-authored-by: criamico Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/language_integrations.ts | 14 ++ .../fleet_integration/overview_component.tsx | 24 --- .../sample/sample_client_readme.tsx | 200 ++++++++++++++++++ .../custom_integrations/public/mocks.tsx | 2 +- .../custom_integrations/public/plugin.tsx | 15 +- .../custom_integrations/public/types.ts | 2 +- .../fleet/.storybook/context/index.tsx | 2 +- .../detail/custom_languages_overview.tsx | 8 +- 8 files changed, 223 insertions(+), 44 deletions(-) delete mode 100644 src/plugins/custom_integrations/public/components/fleet_integration/overview_component.tsx create mode 100644 src/plugins/custom_integrations/public/components/fleet_integration/sample/sample_client_readme.tsx diff --git a/src/plugins/custom_integrations/common/language_integrations.ts b/src/plugins/custom_integrations/common/language_integrations.ts index c9821281f285..9ba914c02fd0 100644 --- a/src/plugins/custom_integrations/common/language_integrations.ts +++ b/src/plugins/custom_integrations/common/language_integrations.ts @@ -145,4 +145,18 @@ export const languageIntegrations: LanguageIntegration[] = [ integrationsAppUrl: `/app/integrations/language_clients/java/overview`, exportLanguageUiComponent: false, }, + // Uncomment to show the sample language client card + README UI + // { + // id: 'sample', + // title: i18n.translate('customIntegrations.languageclients.SampleTitle', { + // defaultMessage: 'Sample Language Client', + // }), + // icon: 'es.svg', + // description: i18n.translate('customIntegrations.languageclients.SampleDescription', { + // defaultMessage: 'Sample language client', + // }), + // docUrlTemplate: '', + // integrationsAppUrl: `/app/integrations/language_clients/sample/overview`, + // exportLanguageUiComponent: true, + // }, ]; diff --git a/src/plugins/custom_integrations/public/components/fleet_integration/overview_component.tsx b/src/plugins/custom_integrations/public/components/fleet_integration/overview_component.tsx deleted file mode 100644 index c5b8584e1e4a..000000000000 --- a/src/plugins/custom_integrations/public/components/fleet_integration/overview_component.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiTitle } from '@elastic/eui'; - -/* -Example of Overview component to be shown inside Integrations app -*/ -export interface ReadmeProps { - packageName: string; -} -export const OverviewComponent: React.FC = ({ packageName }) => { - return ( - -

    {packageName} client - Overview

    -
    - ); -}; diff --git a/src/plugins/custom_integrations/public/components/fleet_integration/sample/sample_client_readme.tsx b/src/plugins/custom_integrations/public/components/fleet_integration/sample/sample_client_readme.tsx new file mode 100644 index 000000000000..c2ca0d62da68 --- /dev/null +++ b/src/plugins/custom_integrations/public/components/fleet_integration/sample/sample_client_readme.tsx @@ -0,0 +1,200 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; + +// eslint-disable-next-line @kbn/eslint/module_migration +import styled from 'styled-components'; +import cuid from 'cuid'; + +import { + EuiButton, + EuiCode, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPageBody, + EuiPageHeader, + EuiPageSection, + EuiSpacer, + EuiText, + EuiTitle, + EuiPanel, + EuiImage, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import icon from '../../../assets/language_clients/es.svg'; + +const CenterColumn = styled(EuiFlexItem)` + max-width: 740px; +`; + +const FixedHeader = styled.div` + width: 100%; + height: 196px; + border-bottom: 1px solid ${euiThemeVars.euiColorLightShade}; +`; + +const IconPanel = styled(EuiPanel)` + padding: ${(props) => props.theme.eui.euiSizeXL}; + width: ${(props) => + parseFloat(props.theme.eui.euiSize) * 6 + parseFloat(props.theme.eui.euiSizeXL) * 2}px; + svg, + img { + height: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px; + width: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px; + } + .euiFlexItem { + height: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px; + justify-content: center; + } +`; + +const TopFlexGroup = styled(EuiFlexGroup)` + max-width: 1150px; + margin-left: auto; + margin-right: auto; + padding: calc(${euiThemeVars.euiSizeXL} * 2) ${euiThemeVars.euiSizeM} 0 ${euiThemeVars.euiSizeM}; +`; + +export const SampleClientReadme = () => { + const [apiKey, setApiKey] = useState(null); + + return ( + <> + + + + + + + + + +

    + +

    +
    +
    +
    +
    + + + + + + + + + + } + /> + + + + +

    + +

    +
    + + + + + {`# Grab the sample language client from NPM and install it in your project \n`} + {`$ npm install @elastic/elasticsearch-sample`} + +
    + + + +

    + +

    +
    + + + + + + + + + + setApiKey(cuid())} disabled={!!apiKey}> + Generate API key + + + + {apiKey && ( + + + {apiKey} + + + )} + +
    + + + +

    + +

    +
    + + + elastic.config.json, + }} + /> + + + + + + {` +{ + "apiKey": "${apiKey || 'YOUR_API_KEY'} +} + + `} + +
    +
    +
    +
    +
    + + ); +}; diff --git a/src/plugins/custom_integrations/public/mocks.tsx b/src/plugins/custom_integrations/public/mocks.tsx index 038fdb178045..2503008ea90e 100644 --- a/src/plugins/custom_integrations/public/mocks.tsx +++ b/src/plugins/custom_integrations/public/mocks.tsx @@ -24,7 +24,7 @@ function createCustomIntegrationsStart(): jest.Mocked { const services = servicesFactory({ startPlugins: {}, coreStart: coreMock.createStart() }); return { - languageClientsUiComponents: new Map(), + languageClientsUiComponents: {}, ContextProvider: jest.fn(({ children }) => ( {children} diff --git a/src/plugins/custom_integrations/public/plugin.tsx b/src/plugins/custom_integrations/public/plugin.tsx index 90a796955a59..827d31ce3749 100755 --- a/src/plugins/custom_integrations/public/plugin.tsx +++ b/src/plugins/custom_integrations/public/plugin.tsx @@ -19,12 +19,10 @@ import { ROUTES_APPEND_CUSTOM_INTEGRATIONS, ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS, } from '../common'; -import { languageIntegrations } from '../common/language_integrations'; - -import { OverviewComponent } from './components/fleet_integration/overview_component'; import { CustomIntegrationsServicesProvider } from './services'; import { servicesFactory } from './services/kibana'; +import { SampleClientReadme } from './components/fleet_integration/sample/sample_client_readme'; export class CustomIntegrationsPlugin implements Plugin @@ -48,16 +46,7 @@ export class CustomIntegrationsPlugin ): CustomIntegrationsStart { const services = servicesFactory({ coreStart, startPlugins }); - const languageClientsUiComponents = new Map(); - - // Set the language clients components to render in Fleet plugin under Integrations app - // Export component only if the integration has exportLanguageUiComponent = true - languageIntegrations - .filter((int) => int.exportLanguageUiComponent) - .map((int) => { - const ReadmeComponent = () => ; - languageClientsUiComponents.set(`language_client.${int.id}`, ReadmeComponent); - }); + const languageClientsUiComponents = { sample: SampleClientReadme }; const ContextProvider: React.FC = ({ children }) => ( diff --git a/src/plugins/custom_integrations/public/types.ts b/src/plugins/custom_integrations/public/types.ts index db8acc3e2e07..60b8aefe23dd 100755 --- a/src/plugins/custom_integrations/public/types.ts +++ b/src/plugins/custom_integrations/public/types.ts @@ -15,7 +15,7 @@ export interface CustomIntegrationsSetup { export interface CustomIntegrationsStart { ContextProvider: React.FC; - languageClientsUiComponents: Map; + languageClientsUiComponents: Record; } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/x-pack/plugins/fleet/.storybook/context/index.tsx b/x-pack/plugins/fleet/.storybook/context/index.tsx index 7ef04979969e..1d5416cf0483 100644 --- a/x-pack/plugins/fleet/.storybook/context/index.tsx +++ b/x-pack/plugins/fleet/.storybook/context/index.tsx @@ -72,7 +72,7 @@ export const StorybookContext: React.FC<{ storyContext?: Parameters }, customIntegrations: { ContextProvider: getStorybookContextProvider(), - languageClientsUiComponents: new Map(), + languageClientsUiComponents: {}, }, docLinks: getDocLinks(), http: getHttp(), diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/custom_languages_overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/custom_languages_overview.tsx index 93e7c6e0fac2..c8c1914599f0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/custom_languages_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/custom_languages_overview.tsx @@ -6,9 +6,10 @@ */ import React from 'react'; import { useParams, Redirect } from 'react-router-dom'; +import { capitalize } from 'lodash'; import { getCustomIntegrationsStart } from '../../../../../../services/custom_integrations'; -import { useLink } from '../../../../../../hooks'; +import { useLink, useBreadcrumbs } from '../../../../hooks'; export interface CustomLanguageClientsParams { pkgkey: string; } @@ -20,10 +21,9 @@ export interface CustomLanguageClientsParams { export const CustomLanguagesOverview = () => { const { pkgkey } = useParams(); const { getPath } = useLink(); + useBreadcrumbs('integration_details_overview', { pkgTitle: capitalize(pkgkey) }); - const Component = getCustomIntegrationsStart().languageClientsUiComponents.get( - `language_client.${pkgkey}` - ); + const Component = getCustomIntegrationsStart().languageClientsUiComponents[pkgkey]; return Component ? : ; }; From 9ad2a9c16c75ed9ca7ce7204e0b86085ef5c2673 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 29 Sep 2022 10:58:43 +0200 Subject: [PATCH 100/185] [Lens] Replace layer dimension groups required flag with more generic one (#141675) * :fire: remove required flag in favour of requiredMinDimensionCount * :ok_hand: Integrate feedback + add more test cases * Update x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx --- .../config_panel/layer_panel.test.tsx | 106 +++++++++++++----- .../editor_frame/config_panel/layer_panel.tsx | 35 +++--- x-pack/plugins/lens/public/types.ts | 1 - .../datatable/visualization.tsx | 2 +- .../gauge/visualization.test.ts | 16 +-- .../visualizations/gauge/visualization.tsx | 4 +- .../heatmap/visualization.test.ts | 18 +-- .../visualizations/heatmap/visualization.tsx | 6 +- .../legacy_metric/visualization.tsx | 2 +- .../__snapshots__/visualization.test.ts.snap | 8 +- .../visualizations/metric/visualization.tsx | 8 +- .../partition/visualization.tsx | 4 +- .../visualizations/xy/annotations/helpers.tsx | 2 +- .../xy/reference_line_helpers.tsx | 2 +- .../visualizations/xy/visualization.test.ts | 6 +- .../visualizations/xy/visualization.tsx | 9 +- 16 files changed, 138 insertions(+), 91 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index baece355d1d5..74394e89f0d6 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -243,7 +243,7 @@ describe('LayerPanel', () => { filterOperations: () => true, supportsMoreColumns: true, dataTestSubj: 'lnsGroup', - required: true, + requiredMinDimensionCount: 1, }, ], }); @@ -303,37 +303,83 @@ describe('LayerPanel', () => { expect(groups.findWhere((e) => e.prop('error') === '')).toHaveLength(1); }); - it('should render the required warning when only one group is configured (with requiredMinDimensionCount)', async () => { - mockVisualization.getConfiguration.mockReturnValue({ - groups: [ - { - groupLabel: 'A', - groupId: 'a', - accessors: [{ columnId: 'x' }], - filterOperations: () => true, - supportsMoreColumns: false, - dataTestSubj: 'lnsGroup', - }, - { - groupLabel: 'B', - groupId: 'b', - accessors: [{ columnId: 'y' }], - filterOperations: () => true, - supportsMoreColumns: true, - dataTestSubj: 'lnsGroup', - requiredMinDimensionCount: 2, - }, - ], - }); - - const { instance } = await mountWithProvider(); + it.each` + minDimensions | accessors | errors + ${1} | ${0} | ${1} + ${2} | ${0} | ${2} + ${2} | ${1} | ${2} + `( + 'should render the required warning for $errors fields when only one group is configured with requiredMinDimensionCount: $minDimensions and $accessors accessors', + async ({ minDimensions, accessors, errors }) => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [{ columnId: 'x' }], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + { + groupLabel: 'B', + groupId: 'b', + accessors: [{ columnId: 'y' }].slice(0, accessors), + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + requiredMinDimensionCount: minDimensions, + }, + ], + }); + const { instance } = await mountWithProvider(); + + const errorMessage = errors === 1 ? 'Requires field' : 'Requires 2 fields'; + + const group = instance.find(EuiFormRow).findWhere((e) => e.prop('error') === errorMessage); + + expect(group).toHaveLength(1); + } + ); + + it.each` + minDimensions | accessors + ${0} | ${0} + ${0} | ${1} + ${1} | ${1} + ${1} | ${2} + ${2} | ${2} + `( + 'should not render the required warning when only one group is configured with requiredMinDimensionCount: $minDimensions and $accessors accessors', + async ({ minDimensions, accessors }) => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [{ columnId: 'x' }], + filterOperations: () => true, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + { + groupLabel: 'B', + groupId: 'b', + accessors: [{ columnId: 'y' }, { columnId: 'z' }].slice(0, accessors), + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + requiredMinDimensionCount: minDimensions, + }, + ], + }); + const { instance } = await mountWithProvider(); - const group = instance - .find(EuiFormRow) - .findWhere((e) => e.prop('error') === 'Requires 2 fields'); + const group = instance.find(EuiFormRow).findWhere((e) => e.prop('error')); - expect(group).toHaveLength(1); - }); + expect(group).toHaveLength(0); + } + ); it('should render the datasource and visualization panels inside the dimension container', async () => { mockVisualization.getConfiguration.mockReturnValueOnce({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index ed6d6b8c0553..cc748df7c3ec 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -381,20 +381,25 @@ export function LayerPanel( let errorText: string = ''; if (!isEmptyLayer) { - if (group.requiredMinDimensionCount) { - errorText = i18n.translate( - 'xpack.lens.editorFrame.requiresTwoOrMoreFieldsWarningLabel', - { - defaultMessage: 'Requires {requiredMinDimensionCount} fields', - values: { - requiredMinDimensionCount: group.requiredMinDimensionCount, - }, - } - ); - } else if (group.required && group.accessors.length === 0) { - errorText = i18n.translate('xpack.lens.editorFrame.requiresFieldWarningLabel', { - defaultMessage: 'Requires field', - }); + if ( + group.requiredMinDimensionCount && + group.requiredMinDimensionCount > group.accessors.length + ) { + if (group.requiredMinDimensionCount > 1) { + errorText = i18n.translate( + 'xpack.lens.editorFrame.requiresTwoOrMoreFieldsWarningLabel', + { + defaultMessage: 'Requires {requiredMinDimensionCount} fields', + values: { + requiredMinDimensionCount: group.requiredMinDimensionCount, + }, + } + ); + } else { + errorText = i18n.translate('xpack.lens.editorFrame.requiresFieldWarningLabel', { + defaultMessage: 'Requires field', + }); + } } else if (group.dimensionsTooMany && group.dimensionsTooMany > 0) { errorText = i18n.translate( 'xpack.lens.editorFrame.tooManyDimensionsSingularWarningLabel', @@ -408,7 +413,7 @@ export function LayerPanel( ); } } - const isOptional = !group.required && !group.suggestedValue; + const isOptional = !group.requiredMinDimensionCount && !group.suggestedValue; return ( !op.isBucketed, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsDatatable_metrics', enableDimensionEditor: true, }, diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts index 94c9ce28987b..e999cac9dd0e 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts @@ -105,7 +105,7 @@ describe('gauge', () => { accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], filterOperations: isNumericDynamicMetric, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, enableFormatSelector: true, @@ -155,7 +155,7 @@ describe('gauge', () => { accessors: [{ columnId: 'goal-accessor' }], filterOperations: isNumericMetric, supportsMoreColumns: false, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsGauge_goalDimensionPanel', enableFormatSelector: false, supportStaticValue: true, @@ -187,7 +187,7 @@ describe('gauge', () => { accessors: [], filterOperations: isNumericDynamicMetric, supportsMoreColumns: true, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, enableFormatSelector: true, @@ -237,7 +237,7 @@ describe('gauge', () => { accessors: [], filterOperations: isNumericMetric, supportsMoreColumns: true, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsGauge_goalDimensionPanel', enableFormatSelector: false, supportStaticValue: true, @@ -275,7 +275,7 @@ describe('gauge', () => { accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], filterOperations: isNumericDynamicMetric, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, enableFormatSelector: true, @@ -325,7 +325,7 @@ describe('gauge', () => { accessors: [{ columnId: 'goal-accessor' }], filterOperations: isNumericMetric, supportsMoreColumns: false, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsGauge_goalDimensionPanel', enableFormatSelector: false, supportStaticValue: true, @@ -368,7 +368,7 @@ describe('gauge', () => { accessors: [{ columnId: 'metric-accessor', triggerIcon: 'none' }], filterOperations: isNumericDynamicMetric, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, enableFormatSelector: true, @@ -422,7 +422,7 @@ describe('gauge', () => { accessors: [{ columnId: 'goal-accessor' }], filterOperations: isNumericMetric, supportsMoreColumns: false, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsGauge_goalDimensionPanel', enableFormatSelector: false, supportStaticValue: true, diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index 37d10918d412..19fd46459b2b 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -275,7 +275,7 @@ export const getGaugeVisualization = ({ : [], filterOperations: isNumericDynamicMetric, supportsMoreColumns: !metricAccessor, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, }, @@ -352,7 +352,7 @@ export const getGaugeVisualization = ({ accessors: state.goalAccessor ? [{ columnId: state.goalAccessor }] : [], filterOperations: isNumericMetric, supportsMoreColumns: !state.goalAccessor, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsGauge_goalDimensionPanel', }, ], diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts index ee6a7030a0c9..38cfa9bf10b5 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.test.ts @@ -136,7 +136,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'x-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_xDimensionPanel', }, { @@ -146,7 +146,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'y-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, { @@ -165,7 +165,7 @@ describe('heatmap', () => { ], filterOperations: isCellValueSupported, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_cellPanel', enableDimensionEditor: true, }, @@ -194,7 +194,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'x-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_xDimensionPanel', }, { @@ -204,7 +204,7 @@ describe('heatmap', () => { accessors: [], filterOperations: filterOperationsAxis, supportsMoreColumns: true, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, { @@ -217,7 +217,7 @@ describe('heatmap', () => { accessors: [], filterOperations: isCellValueSupported, supportsMoreColumns: true, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_cellPanel', enableDimensionEditor: true, }, @@ -250,7 +250,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'x-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_xDimensionPanel', }, { @@ -260,7 +260,7 @@ describe('heatmap', () => { accessors: [{ columnId: 'y-accessor' }], filterOperations: filterOperationsAxis, supportsMoreColumns: false, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, { @@ -278,7 +278,7 @@ describe('heatmap', () => { ], filterOperations: isCellValueSupported, supportsMoreColumns: false, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_cellPanel', enableDimensionEditor: true, }, diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx index 09548df0a67e..1fc48b4d71c3 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx @@ -181,7 +181,7 @@ export const getHeatmapVisualization = ({ accessors: state.xAccessor ? [{ columnId: state.xAccessor }] : [], filterOperations: filterOperationsAxis, supportsMoreColumns: !state.xAccessor, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_xDimensionPanel', }, { @@ -191,7 +191,7 @@ export const getHeatmapVisualization = ({ accessors: state.yAccessor ? [{ columnId: state.yAccessor }] : [], filterOperations: filterOperationsAxis, supportsMoreColumns: !state.yAccessor, - required: false, + requiredMinDimensionCount: 0, dataTestSubj: 'lnsHeatmap_yDimensionPanel', }, { @@ -224,7 +224,7 @@ export const getHeatmapVisualization = ({ filterOperations: isCellValueSupported, supportsMoreColumns: !state.valueAccessor, enableDimensionEditor: true, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsHeatmap_cellPanel', }, ], diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx index f1b1ffd1f00b..02a4cd23ad4f 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx @@ -258,7 +258,7 @@ export const getLegacyMetricVisualization = ({ filterOperations: (op: OperationMetadata) => !op.isBucketed && legacyMetricSupportedTypes.has(op.dataType), enableDimensionEditor: true, - required: true, + requiredMinDimensionCount: 1, }, ], }; diff --git a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap index 8c565cbed53b..7d1df6815826 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap @@ -24,7 +24,7 @@ Object { "paramEditorCustomProps": Object { "headingLabel": "Value", }, - "required": true, + "requiredMinDimensionCount": 1, "supportsMoreColumns": false, }, Object { @@ -46,7 +46,7 @@ Object { "paramEditorCustomProps": Object { "headingLabel": "Value", }, - "required": false, + "requiredMinDimensionCount": 0, "supportsMoreColumns": false, }, Object { @@ -70,7 +70,7 @@ Object { "headingLabel": "Value", }, "prioritizedOperation": "max", - "required": false, + "requiredMinDimensionCount": 0, "supportStaticValue": true, "supportsMoreColumns": false, }, @@ -91,7 +91,7 @@ Object { "groupId": "breakdownBy", "groupLabel": "Break down by", "layerId": "first", - "required": false, + "requiredMinDimensionCount": 0, "supportsMoreColumns": false, }, ], diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index b58068b3ec20..81f7f08fe322 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -315,7 +315,7 @@ export const getMetricVisualization = ({ enableDimensionEditor: true, enableFormatSelector: true, formatSelectorOptions: formatterOptions, - required: true, + requiredMinDimensionCount: 1, }, { groupId: GROUP_ID.SECONDARY_METRIC, @@ -341,7 +341,7 @@ export const getMetricVisualization = ({ enableDimensionEditor: true, enableFormatSelector: true, formatSelectorOptions: formatterOptions, - required: false, + requiredMinDimensionCount: 0, }, { groupId: GROUP_ID.MAX, @@ -367,7 +367,7 @@ export const getMetricVisualization = ({ formatSelectorOptions: formatterOptions, supportStaticValue: true, prioritizedOperation: 'max', - required: false, + requiredMinDimensionCount: 0, groupTooltip: i18n.translate('xpack.lens.metric.maxTooltip', { defaultMessage: 'If the maximum value is specified, the minimum value is fixed at zero.', @@ -393,7 +393,7 @@ export const getMetricVisualization = ({ enableDimensionEditor: true, enableFormatSelector: true, formatSelectorOptions: formatterOptions, - required: false, + requiredMinDimensionCount: 0, }, ], }; diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index a60578aa1f0b..2a8bdc2dd4ab 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -168,7 +168,7 @@ export const getPieVisualization = ({ } const primaryGroupConfigBaseProps = { - required: true, + requiredMinDimensionCount: 1, groupId: 'primaryGroups', accessors, enableDimensionEditor: true, @@ -284,7 +284,7 @@ export const getPieVisualization = ({ accessors: layer.metric ? [{ columnId: layer.metric }] : [], supportsMoreColumns: !layer.metric, filterOperations: numberMetricOperations, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsPie_sizeByDimensionPanel', }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index 34aff2658277..baaed78ec023 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -414,7 +414,7 @@ export const getAnnotationsConfiguration = ({ invalidMessage: i18n.translate('xpack.lens.xyChart.addAnnotationsLayerLabelDisabledHelp', { defaultMessage: 'Annotations require a time based chart to work. Add a date histogram.', }), - required: false, + requiredMinDimensionCount: 0, supportsMoreColumns: true, supportFieldFormat: false, enableDimensionEditor: true, diff --git a/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx index 362b63c46eb2..84f0a35d3b95 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.tsx @@ -461,7 +461,7 @@ export const getReferenceConfiguration = ({ accessors: config.map(({ forAccessor, color }) => getSingleColorConfig(forAccessor, color)), filterOperations: isNumericMetric, supportsMoreColumns: true, - required: false, + requiredMinDimensionCount: 0, enableDimensionEditor: true, supportStaticValue: true, paramEditorCustomProps: { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 818df64fba94..556a89c9a855 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -1047,7 +1047,7 @@ describe('xy_visualization', () => { frame, layerId: 'first', }).groups; - expect(splitGroup.required).toBe(true); + expect(splitGroup.requiredMinDimensionCount).toBe(1); }); test.each([ @@ -1087,7 +1087,7 @@ describe('xy_visualization', () => { frame, layerId: 'first', }).groups; - expect(splitGroup.required).toBe(false); + expect(splitGroup.requiredMinDimensionCount).toBe(0); } ); @@ -1236,7 +1236,7 @@ describe('xy_visualization', () => { frame, layerId: 'first', }).groups; - expect(splitGroup.required).toBe(true); + expect(splitGroup.requiredMinDimensionCount).toBe(1); } ); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 61bdd4151219..34ab6c88ffa1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -276,10 +276,6 @@ export const getXyVisualization = ({ accessors: sortedAccessors, }); - if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); - } - const dataLayer: XYDataLayerConfig = layer; const dataLayers = getDataLayers(state.layers); @@ -332,7 +328,7 @@ export const getXyVisualization = ({ accessors: mappedAccessors, filterOperations: isNumericDynamicMetric, supportsMoreColumns: true, - required: true, + requiredMinDimensionCount: 1, dataTestSubj: 'lnsXY_yDimensionPanel', enableDimensionEditor: true, }, @@ -357,7 +353,8 @@ export const getXyVisualization = ({ filterOperations: isBucketed, supportsMoreColumns: !dataLayer.splitAccessor, dataTestSubj: 'lnsXY_splitDimensionPanel', - required: dataLayer.seriesType.includes('percentage') && hasOnlyOneAccessor, + requiredMinDimensionCount: + dataLayer.seriesType.includes('percentage') && hasOnlyOneAccessor ? 1 : 0, enableDimensionEditor: true, }, ], From ef73713337ea217ff8c4602696f2b0328f3e50b2 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Sep 2022 11:01:48 +0200 Subject: [PATCH 101/185] clear state transfer on navigating away (#141969) --- x-pack/plugins/lens/public/app_plugin/mounter.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 7cf1331282d4..ef1933dbd5ca 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -386,5 +386,6 @@ export async function mountApp( lensServices.inspector.close(); unlistenParentHistory(); lensStore.dispatch(navigateAway()); + stateTransfer.clearEditorState?.(APP_ID); }; } From 226efc0c1f54d634880d5399ce2b85b384f6a19f Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Sep 2022 11:02:11 +0200 Subject: [PATCH 102/185] [Lens] Quickfunction help (#141399) * quickfunction help * adjust copy * Update x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/differences.tsx Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/dimension_editor.tsx | 91 ++++++++++++++++++- .../definitions/calculations/counter_rate.tsx | 8 ++ .../calculations/cumulative_sum.tsx | 8 ++ .../definitions/calculations/differences.tsx | 8 ++ .../calculations/moving_average.tsx | 8 ++ .../operations/definitions/cardinality.tsx | 8 ++ .../operations/definitions/count.tsx | 5 + .../operations/definitions/date_histogram.tsx | 8 ++ .../definitions/filters/filters.tsx | 8 ++ .../operations/definitions/index.ts | 1 + .../operations/definitions/last_value.tsx | 8 ++ .../operations/definitions/metrics.tsx | 40 ++++++++ .../operations/definitions/percentile.tsx | 8 ++ .../definitions/percentile_ranks.tsx | 8 ++ .../operations/definitions/ranges/ranges.tsx | 5 + .../operations/definitions/terms/index.tsx | 5 + 16 files changed, 224 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index fbd9a5650013..5a975482e625 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -20,6 +20,11 @@ import { useEuiTheme, EuiFlexGroup, EuiFlexItem, + EuiPopover, + EuiPopoverTitle, + EuiPanel, + EuiBasicTable, + EuiButtonIcon, } from '@elastic/eui'; import ReactDOM from 'react-dom'; import type { IndexPatternDimensionEditorProps } from './dimension_panel'; @@ -116,6 +121,10 @@ export function DimensionEditor(props: DimensionEditorProps) { selectedColumn && operationDefinitionMap[selectedColumn.operationType]; const [temporaryState, setTemporaryState] = useState('none'); + const [isHelpOpen, setIsHelpOpen] = useState(false); + + const onHelpClick = () => setIsHelpOpen((prevIsHelpOpen) => !prevIsHelpOpen); + const closeHelp = () => setIsHelpOpen(false); const temporaryQuickFunction = Boolean(temporaryState === quickFunctionsName); const temporaryStaticValue = Boolean(temporaryState === staticValueOperationName); @@ -596,12 +605,88 @@ export function DimensionEditor(props: DimensionEditorProps) { ...services, }; + const helpButton = ; + + const columnsSidebar = [ + { + field: 'function', + name: i18n.translate('xpack.lens.indexPattern.functionTable.functionHeader', { + defaultMessage: 'Function', + }), + width: '150px', + }, + { + field: 'description', + name: i18n.translate('xpack.lens.indexPattern.functionTable.descriptionHeader', { + defaultMessage: 'Description', + }), + }, + ]; + + const items = sideNavItems + .filter((item) => operationDefinitionMap[item.id!].quickFunctionDocumentation) + .map((item) => { + const operationDefinition = operationDefinitionMap[item.id!]!; + return { + id: item.id!, + function: operationDefinition.displayName, + description: operationDefinition.quickFunctionDocumentation!, + }; + }); + const quickFunctions = ( <> + + + + {i18n.translate('xpack.lens.indexPattern.quickFunctions.popoverTitle', { + defaultMessage: 'Quick functions', + })} + + + + + + + + {i18n.translate('xpack.lens.indexPattern.functionsLabel', { + defaultMessage: 'Functions', + })} + + + } fullWidth > ); }, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.dateHistogram.documentation.quick', + { + defaultMessage: ` +The date or date range values distributed into intervals. + `, + } + ), }; function parseInterval(currentInterval: string) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index da06ea8ae1bf..b972129109e1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -158,6 +158,14 @@ export const filtersOperation: OperationDefinition< }, getMaxPossibleNumValues: (column) => column.params.filters.length, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.filters.documentation.quick', + { + defaultMessage: ` + Divides values into predefined subsets. + `, + } + ), }; export const FilterList = ({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 5ce13adbf5ca..6c79d314d10f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -364,6 +364,7 @@ interface BaseOperationDefinitionProps< description: string; section: 'elasticsearch' | 'calculation'; }; + quickFunctionDocumentation?: string; /** * React component for operation field specific behaviour */ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 2709d22b2a32..4206cd810976 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -428,4 +428,12 @@ Example: Get the current status of server A: `, }), }, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.lastValue.documentation.quick', + { + defaultMessage: ` +The value of a field from the last document, ordered by the default time field of the data view. + `, + } + ), }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index 09dc8576a042..e8c8c54c1cef 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -60,6 +60,7 @@ function buildMetricOperation>({ hideZeroOption, aggConfigParams, documentationDescription, + quickFunctionDocumentation, }: { type: T['operationType']; displayName: string; @@ -71,6 +72,7 @@ function buildMetricOperation>({ hideZeroOption?: boolean; aggConfigParams?: Record; documentationDescription?: string; + quickFunctionDocumentation?: string; }) { const labelLookup = (name: string, column?: BaseIndexPatternColumn) => { const label = ofName(name); @@ -240,6 +242,7 @@ Example: Get the {metric} of price for orders from the UK: }, }), }, + quickFunctionDocumentation, shiftable: true, } as OperationDefinition; } @@ -265,6 +268,12 @@ export const minOperation = buildMetricOperation({ defaultMessage: 'A single-value metrics aggregation that returns the minimum value among the numeric values extracted from the aggregated documents.', }), + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.min.quickFunctionDescription', + { + defaultMessage: 'The minimum value of a number field.', + } + ), supportsDate: true, }); @@ -282,6 +291,12 @@ export const maxOperation = buildMetricOperation({ defaultMessage: 'A single-value metrics aggregation that returns the maximum value among the numeric values extracted from the aggregated documents.', }), + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.max.quickFunctionDescription', + { + defaultMessage: 'The maximum value of a number field.', + } + ), supportsDate: true, }); @@ -300,6 +315,12 @@ export const averageOperation = buildMetricOperation({ defaultMessage: 'A single-value metric aggregation that computes the average of numeric values that are extracted from the aggregated documents', }), + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.avg.quickFunctionDescription', + { + defaultMessage: 'The average value of a number field.', + } + ), }); export const standardDeviationOperation = buildMetricOperation( @@ -334,6 +355,13 @@ To get the variance of price for orders from the UK, use \`square(standard_devia `, } ), + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.standardDeviation.quickFunctionDescription', + { + defaultMessage: + 'The standard deviation of the values of a number field which is the amount of variation of the fields values.', + } + ), } ); @@ -354,6 +382,12 @@ export const sumOperation = buildMetricOperation({ 'A single-value metrics aggregation that sums up numeric values that are extracted from the aggregated documents.', }), hideZeroOption: true, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.sum.quickFunctionDescription', + { + defaultMessage: 'The total amount of the values of a number field.', + } + ), }); export const medianOperation = buildMetricOperation({ @@ -371,4 +405,10 @@ export const medianOperation = buildMetricOperation({ defaultMessage: 'A single-value metrics aggregation that computes the median value that are extracted from the aggregated documents.', }), + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.median.quickFunctionDescription', + { + defaultMessage: 'The median value of a number field.', + } + ), }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx index 3bc91b91ed3d..ec4a3d569e7d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx @@ -417,4 +417,12 @@ Example: Get the number of bytes larger than 95 % of values: `, }), }, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.percentile.documentation.quick', + { + defaultMessage: ` + The largest value that is smaller than n percent of the values that occur in all documents. + `, + } + ), }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx index 7cb8d7ea64ae..45bd05f37372 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile_ranks.tsx @@ -266,4 +266,12 @@ Example: Get the percentage of values which are below of 100: `, }), }, + quickFunctionDocumentation: i18n.translate( + 'xpack.lens.indexPattern.percentileRanks.documentation.quick', + { + defaultMessage: ` +The percentage of values that are below a specific value. For example, when a value is greater than or equal to 95% of the calculated values, the value is the 95th percentile rank. + `, + } + ), }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index aa84c727ae50..52c9471aefe0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -267,4 +267,9 @@ export const rangeOperation: OperationDefinition< /> ); }, + quickFunctionDocumentation: i18n.translate('xpack.lens.indexPattern.ranges.documentation.quick', { + defaultMessage: ` + Buckets values along defined numeric ranges. + `, + }), }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 9017e91bff08..1f4b4846906d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -557,6 +557,11 @@ export const termsOperation: OperationDefinition< ); }, + quickFunctionDocumentation: i18n.translate('xpack.lens.indexPattern.terms.documentation.quick', { + defaultMessage: ` +The top values of a specified field ranked by the chosen metric. + `, + }), paramEditor: function ParamEditor({ layer, paramEditorUpdater, From 574e96a868ccd05be0fde59b09ef8cd03e7e6a15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 11:38:30 +0200 Subject: [PATCH 103/185] Update dependency vega-lite to ^5.5.0 (#137794) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index eedb8db4b605..3bbcabba0cdd 100644 --- a/package.json +++ b/package.json @@ -642,7 +642,7 @@ "uuid": "3.3.2", "vega": "^5.22.1", "vega-interpreter": "^1.0.4", - "vega-lite": "^5.3.0", + "vega-lite": "^5.5.0", "vega-schema-url-parser": "^2.2.0", "vega-spec-injector": "^0.0.2", "vega-tooltip": "^0.28.0", diff --git a/yarn.lock b/yarn.lock index 4996c786772d..804502ebeb4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27950,10 +27950,10 @@ vega-label@~1.2.0: vega-scenegraph "^4.9.2" vega-util "^1.15.2" -vega-lite@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.3.0.tgz#b9b9ecd80e869e823e6848c67d0a8ad94954bdee" - integrity sha512-6giodZ/bJnWyLq6Gj4OyiDt7EndoGyC9f5xDQjo82yPpUiO4MuG9iiPMqR1SPKmG9/qPBf+klWQR0v/7Mgju0Q== +vega-lite@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.5.0.tgz#07345713d538cd63278748ec119c261722be66ff" + integrity sha512-MQBJt/iaUegvhRTS/hZVWfMOSF5ai4awlR2qtwTgHd84bErf9v7GtaZ9ArhJqXCb+FizvZ2jatmoYCzovgAhkg== dependencies: "@types/clone" "~2.1.1" array-flat-polyfill "^1.0.1" From d915169c5038ef962347448229d095532c463f12 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 29 Sep 2022 10:43:27 +0100 Subject: [PATCH 104/185] [Security Solution] Update visualizationType (#141235) * update visualizationType * update labels * add snapshots for charts --- .../__snapshots__/authentication.test.ts.snap | 276 ++++++++++++++++++ .../__snapshots__/external_alert.test.ts.snap | 231 +++++++++++++++ .../common/authentication.test.ts | 45 +++ .../lens_attributes/common/authentication.ts | 8 +- .../common/external_alert.test.ts | 45 +++ .../lens_attributes/common/external_alert.ts | 5 +- .../hosts/__snapshots__/event.test.ts.snap | 205 +++++++++++++ .../__snapshots__/kpi_host_area.test.ts.snap | 196 +++++++++++++ .../kpi_host_metric.test.ts.snap | 143 +++++++++ .../kpi_unique_ips_area.test.ts.snap | 255 ++++++++++++++++ .../kpi_unique_ips_bar.test.ts.snap | 265 +++++++++++++++++ ...unique_ips_destination_metric.test.ts.snap | 143 +++++++++ .../kpi_unique_ips_source_metric.test.ts.snap | 143 +++++++++ .../lens_attributes/hosts/event.test.ts | 45 +++ .../hosts/kpi_host_area.test.ts | 45 +++ .../lens_attributes/hosts/kpi_host_area.ts | 3 +- .../hosts/kpi_host_metric.test.ts | 45 +++ .../lens_attributes/hosts/kpi_host_metric.ts | 2 +- .../hosts/kpi_unique_ips_area.test.ts | 45 +++ .../hosts/kpi_unique_ips_bar.test.ts | 45 +++ .../hosts/kpi_unique_ips_bar.ts | 6 +- .../kpi_unique_ips_destination_metric.test.ts | 45 +++ .../kpi_unique_ips_destination_metric.ts | 2 +- .../kpi_unique_ips_source_metric.test.ts | 45 +++ .../hosts/kpi_unique_ips_source_metric.ts | 2 +- .../dns_top_domains.test.ts.snap | 245 ++++++++++++++++ .../kpi_dns_queries.test.ts.snap | 189 ++++++++++++ .../kpi_network_events.test.ts.snap | 193 ++++++++++++ .../kpi_tls_handshakes.test.ts.snap | 212 ++++++++++++++ .../kpi_unique_flow_ids.test.ts.snap | 176 +++++++++++ .../kpi_unique_private_ips_area.test.ts.snap | 261 +++++++++++++++++ .../kpi_unique_private_ips_bar.test.ts.snap | 276 ++++++++++++++++++ ...rivate_ips_destination_metric.test.ts.snap | 149 ++++++++++ ...que_private_ips_source_metric.test.ts.snap | 149 ++++++++++ .../network/dns_top_domains.test.ts | 45 +++ .../network/dns_top_domains.ts | 5 +- .../network/kpi_dns_queries.test.ts | 45 +++ .../network/kpi_dns_queries.ts | 2 +- .../network/kpi_network_events.test.ts | 45 +++ .../network/kpi_network_events.ts | 2 +- .../network/kpi_tls_handshakes.test.ts | 45 +++ .../network/kpi_tls_handshakes.ts | 2 +- .../network/kpi_unique_flow_ids.test.ts | 45 +++ .../network/kpi_unique_flow_ids.ts | 2 +- .../kpi_unique_private_ips_area.test.ts | 45 +++ .../network/kpi_unique_private_ips_area.ts | 5 +- .../kpi_unique_private_ips_bar.test.ts | 45 +++ .../network/kpi_unique_private_ips_bar.ts | 6 +- ...que_private_ips_destination_metric.test.ts | 45 +++ ...i_unique_private_ips_destination_metric.ts | 2 +- ...i_unique_private_ips_source_metric.test.ts | 45 +++ .../kpi_unique_private_ips_source_metric.ts | 2 +- .../kpi_total_users_area.test.ts.snap | 151 ++++++++++ .../kpi_total_users_metric.test.ts.snap | 97 ++++++ ...authentication_metric_failure.test.ts.snap | 126 ++++++++ ...kpi_user_authentications_area.test.ts.snap | 240 +++++++++++++++ .../kpi_user_authentications_bar.test.ts.snap | 240 +++++++++++++++ ...uthentications_metric_success.test.ts.snap | 127 ++++++++ .../users/kpi_total_users_area.test.ts | 45 +++ .../users/kpi_total_users_area.ts | 3 +- .../users/kpi_total_users_metric.test.ts | 45 +++ .../users/kpi_total_users_metric.ts | 4 +- ...user_authentication_metric_failure.test.ts | 45 +++ .../kpi_user_authentication_metric_failure.ts | 2 +- .../kpi_user_authentications_area.test.ts | 45 +++ .../users/kpi_user_authentications_area.ts | 5 +- .../kpi_user_authentications_bar.test.ts | 45 +++ ...ser_authentications_metric_success.test.ts | 45 +++ ...kpi_user_authentications_metric_success.ts | 2 +- .../visualization_actions/mocks.tsx | 65 +++++ .../visualization_actions/translations.ts | 32 ++ .../use_lens_attributes.test.tsx | 65 +---- 72 files changed, 5909 insertions(+), 93 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/event.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/event.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_area.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_bar.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/mocks.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap new file mode 100644 index 000000000000..747487203066 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap @@ -0,0 +1,276 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`authenticationLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-3fd0c5d5-f762-4a27-8c56-14eee0223e13", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-bef502be-e5ff-442f-9e3e-229f86ca2afa", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "6f4dbdc7-35b6-4e20-ac53-1272167e3919", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "3fd0c5d5-f762-4a27-8c56-14eee0223e13": Object { + "columnOrder": Array [ + "b41a2958-650b-470a-84c4-c6fd8f0c6d37", + "5417777d-d9d9-4268-9cdc-eb29b873bd65", + ], + "columns": Object { + "5417777d-d9d9-4268-9cdc-eb29b873bd65": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome : \\"success\\"", + }, + "isBucketed": false, + "label": "Success", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "b41a2958-650b-470a-84c4-c6fd8f0c6d37": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + "bef502be-e5ff-442f-9e3e-229f86ca2afa": Object { + "columnOrder": Array [ + "cded27f7-8ef8-458c-8d9b-70db48ae340d", + "a3bf9dc1-c8d2-42d6-9e60-31892a4c509e", + ], + "columns": Object { + "a3bf9dc1-c8d2-42d6-9e60-31892a4c509e": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome : \\"failure\\"", + }, + "isBucketed": false, + "label": "Failure", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "cded27f7-8ef8-458c-8d9b-70db48ae340d": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "6f4dbdc7-35b6-4e20-ac53-1272167e3919", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"must\\":[{\\"term\\":{\\"event.category\\":\\"authentication\\"}}]}}", + }, + "query": Object { + "bool": Object { + "must": Array [ + Object { + "term": Object { + "event.category": "authentication", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "layers": Array [ + Object { + "accessors": Array [ + "5417777d-d9d9-4268-9cdc-eb29b873bd65", + ], + "layerId": "3fd0c5d5-f762-4a27-8c56-14eee0223e13", + "layerType": "data", + "position": "top", + "seriesType": "bar_stacked", + "showGridlines": false, + "xAccessor": "b41a2958-650b-470a-84c4-c6fd8f0c6d37", + "yConfig": Array [ + Object { + "color": "#54b399", + "forAccessor": "5417777d-d9d9-4268-9cdc-eb29b873bd65", + }, + ], + }, + Object { + "accessors": Array [ + "a3bf9dc1-c8d2-42d6-9e60-31892a4c509e", + ], + "layerId": "bef502be-e5ff-442f-9e3e-229f86ca2afa", + "layerType": "data", + "seriesType": "bar_stacked", + "xAccessor": "cded27f7-8ef8-458c-8d9b-70db48ae340d", + "yConfig": Array [ + Object { + "color": "#da8b45", + "forAccessor": "a3bf9dc1-c8d2-42d6-9e60-31892a4c509e", + }, + ], + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "bar_stacked", + "title": "Empty XY chart", + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "Authentication", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap new file mode 100644 index 000000000000..ac42b228012f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap @@ -0,0 +1,231 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getExternalAlertLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-a3c54471-615f-4ff9-9fda-69b5b2ea3eef", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "723c4653-681b-4105-956e-abef287bf025", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "a04472fc-94a3-4b8d-ae05-9d30ea8fbd6a", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "a3c54471-615f-4ff9-9fda-69b5b2ea3eef": Object { + "columnOrder": Array [ + "42334c6e-98d9-47a2-b4cb-a445abb44c93", + "37bdf546-3c11-4b08-8c5d-e37debc44f1d", + "0a923af2-c880-4aa3-aa93-a0b9c2801f6d", + ], + "columns": Object { + "0a923af2-c880-4aa3-aa93-a0b9c2801f6d": Object { + "dataType": "number", + "isBucketed": false, + "label": "Count of records", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "37bdf546-3c11-4b08-8c5d-e37debc44f1d": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "42334c6e-98d9-47a2-b4cb-a445abb44c93": Object { + "dataType": "string", + "isBucketed": true, + "label": "Top values of event.dataset", + "operationType": "terms", + "params": Object { + "missingBucket": false, + "orderBy": Object { + "columnId": "0a923af2-c880-4aa3-aa93-a0b9c2801f6d", + "type": "column", + }, + "orderDirection": "desc", + "otherBucket": true, + "parentFormat": Object { + "id": "terms", + }, + "size": 10, + }, + "scale": "ordinal", + "sourceField": "event.dataset", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "a04472fc-94a3-4b8d-ae05-9d30ea8fbd6a", + "key": "event.kind", + "negate": false, + "params": Object { + "query": "alert", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "event.kind": "alert", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "layers": Array [ + Object { + "accessors": Array [ + "0a923af2-c880-4aa3-aa93-a0b9c2801f6d", + ], + "layerId": "a3c54471-615f-4ff9-9fda-69b5b2ea3eef", + "layerType": "data", + "position": "top", + "seriesType": "bar_stacked", + "showGridlines": false, + "splitAccessor": "42334c6e-98d9-47a2-b4cb-a445abb44c93", + "xAccessor": "37bdf546-3c11-4b08-8c5d-e37debc44f1d", + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "bar_stacked", + "title": "Empty XY chart", + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "External alerts", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.test.ts new file mode 100644 index 000000000000..808789db397e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { authenticationLensAttributes } from './authentication'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('authenticationLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: authenticationLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts index 199f78d372cd..15d7f029ae61 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { + AUTHENCICATION_FAILURE_CHART_LABEL, + AUTHENCICATION_SUCCESS_CHART_LABEL, +} from '../../translations'; import type { LensAttributes } from '../../types'; export const authenticationLensAttributes: LensAttributes = { @@ -110,7 +114,7 @@ export const authenticationLensAttributes: LensAttributes = { }, }, '5417777d-d9d9-4268-9cdc-eb29b873bd65': { - label: 'Success', + label: AUTHENCICATION_SUCCESS_CHART_LABEL, dataType: 'number', operationType: 'count', isBucketed: false, @@ -143,7 +147,7 @@ export const authenticationLensAttributes: LensAttributes = { }, }, 'a3bf9dc1-c8d2-42d6-9e60-31892a4c509e': { - label: 'Failure', + label: AUTHENCICATION_FAILURE_CHART_LABEL, dataType: 'number', operationType: 'count', isBucketed: false, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.test.ts new file mode 100644 index 000000000000..da890c4d49fe --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { getExternalAlertLensAttributes } from './external_alert'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('getExternalAlertLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getExternalAlertLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.ts index e24db03b3302..a815d442b043 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/external_alert.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { COUNT, TOP_VALUE } from '../../translations'; import type { GetLensAttributes, LensAttributes } from '../../types'; export const getExternalAlertLensAttributes: GetLensAttributes = ( @@ -86,7 +87,7 @@ export const getExternalAlertLensAttributes: GetLensAttributes = ( }, }, '0a923af2-c880-4aa3-aa93-a0b9c2801f6d': { - label: 'Count of records', + label: COUNT, dataType: 'number', operationType: 'count', isBucketed: false, @@ -94,7 +95,7 @@ export const getExternalAlertLensAttributes: GetLensAttributes = ( sourceField: '___records___', }, '42334c6e-98d9-47a2-b4cb-a445abb44c93': { - label: `Top values of ${stackByField}`, // could be event.category + label: TOP_VALUE(`${stackByField}`), // could be event.category dataType: 'string', operationType: 'terms', scale: 'ordinal', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/event.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/event.test.ts.snap new file mode 100644 index 000000000000..7f3fdb6c6610 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/event.test.ts.snap @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getEventsHistogramLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-0039eb0c-9a1a-4687-ae54-0f4e239bec75", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "0039eb0c-9a1a-4687-ae54-0f4e239bec75": Object { + "columnOrder": Array [ + "34919782-4546-43a5-b668-06ac934d3acd", + "aac9d7d0-13a3-480a-892b-08207a787926", + "e09e0380-0740-4105-becc-0a4ca12e3944", + ], + "columns": Object { + "34919782-4546-43a5-b668-06ac934d3acd": Object { + "dataType": "string", + "isBucketed": true, + "label": "Top values of event.dataset", + "operationType": "terms", + "params": Object { + "missingBucket": false, + "orderBy": Object { + "columnId": "e09e0380-0740-4105-becc-0a4ca12e3944", + "type": "column", + }, + "orderDirection": "asc", + "otherBucket": true, + "parentFormat": Object { + "id": "terms", + }, + "size": 10, + }, + "scale": "ordinal", + "sourceField": "event.dataset", + }, + "aac9d7d0-13a3-480a-892b-08207a787926": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "e09e0380-0740-4105-becc-0a4ca12e3944": Object { + "dataType": "number", + "isBucketed": false, + "label": "Count of records", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "layers": Array [ + Object { + "accessors": Array [ + "e09e0380-0740-4105-becc-0a4ca12e3944", + ], + "layerId": "0039eb0c-9a1a-4687-ae54-0f4e239bec75", + "layerType": "data", + "position": "top", + "seriesType": "bar_stacked", + "showGridlines": false, + "splitAccessor": "34919782-4546-43a5-b668-06ac934d3acd", + "xAccessor": "aac9d7d0-13a3-480a-892b-08207a787926", + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "bar_stacked", + "title": "Empty XY chart", + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "Host - events", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap new file mode 100644 index 000000000000..c5e4e272ec9c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap @@ -0,0 +1,196 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiHostAreaLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-416b6fad-1923-4f6a-a2df-b223bb287e30", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "416b6fad-1923-4f6a-a2df-b223bb287e30": Object { + "columnOrder": Array [ + "5eea817b-67b7-4268-8ecb-7688d1094721", + "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + ], + "columns": Object { + "5eea817b-67b7-4268-8ecb-7688d1094721": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "b00c65ea-32be-4163-bfc8-f795b1ef9d06": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Unique count of host.name", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "host.name", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": false, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + ], + "layerId": "416b6fad-1923-4f6a-a2df-b223bb287e30", + "layerType": "data", + "seriesType": "area", + "xAccessor": "5eea817b-67b7-4268-8ecb-7688d1094721", + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "area", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Host] Hosts - area", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap new file mode 100644 index 000000000000..3669de2d3010 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap @@ -0,0 +1,143 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiHostMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-416b6fad-1923-4f6a-a2df-b223bb287e30", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "416b6fad-1923-4f6a-a2df-b223bb287e30": Object { + "columnOrder": Array [ + "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + ], + "columns": Object { + "b00c65ea-32be-4163-bfc8-f795b1ef9d06": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "host.name", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + "layerId": "416b6fad-1923-4f6a-a2df-b223bb287e30", + "layerType": "data", + }, + }, + "title": "[Host] Hosts - metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap new file mode 100644 index 000000000000..acaf78556269 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniqueIpsAreaLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-ca05ecdb-0fa4-49a8-9305-b23d91012a46", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "8be0156b-d423-4a39-adf1-f54d4c9f2e69": Object { + "columnOrder": Array [ + "a0cb6400-f708-46c3-ad96-24788f12dae4", + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + ], + "columns": Object { + "a0cb6400-f708-46c3-ad96-24788f12dae4": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Src.", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + }, + "incompleteColumns": Object {}, + }, + "ca05ecdb-0fa4-49a8-9305-b23d91012a46": Object { + "columnOrder": Array [ + "f95e74e6-99dd-4b11-8faf-439b4d959df9", + "e7052671-fb9e-481f-8df3-7724c98cfc6f", + ], + "columns": Object { + "e7052671-fb9e-481f-8df3-7724c98cfc6f": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Dest.", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + "f95e74e6-99dd-4b11-8faf-439b4d959df9": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + ], + "layerId": "8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "layerType": "data", + "seriesType": "area", + "xAccessor": "a0cb6400-f708-46c3-ad96-24788f12dae4", + "yConfig": Array [ + Object { + "color": "#d36186", + "forAccessor": "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + }, + ], + }, + Object { + "accessors": Array [ + "e7052671-fb9e-481f-8df3-7724c98cfc6f", + ], + "layerId": "ca05ecdb-0fa4-49a8-9305-b23d91012a46", + "layerType": "data", + "seriesType": "area", + "xAccessor": "f95e74e6-99dd-4b11-8faf-439b4d959df9", + "yConfig": Array [ + Object { + "color": "#9170b8", + "forAccessor": "e7052671-fb9e-481f-8df3-7724c98cfc6f", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "area", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Host] Unique IPs - area", + "type": "lens", + "updated_at": "2022-02-09T17:44:03.359Z", + "version": "WzI5MTI5OSwzXQ==", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap new file mode 100644 index 000000000000..9f702ecb0641 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap @@ -0,0 +1,265 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniqueIpsBarLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-ec84ba70-2adb-4647-8ef0-8ad91a0e6d4e", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "8be0156b-d423-4a39-adf1-f54d4c9f2e69": Object { + "columnOrder": Array [ + "f8bfa719-5c1c-4bf2-896e-c318d77fc08e", + "32f66676-f4e1-48fd-b7f8-d4de38318601", + ], + "columns": Object { + "32f66676-f4e1-48fd-b7f8-d4de38318601": Object { + "dataType": "number", + "isBucketed": false, + "label": "Unique count of source.ip", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + "f8bfa719-5c1c-4bf2-896e-c318d77fc08e": Object { + "dataType": "string", + "isBucketed": true, + "label": "Filters", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "", + }, + "label": "Src.", + }, + ], + }, + "scale": "ordinal", + }, + }, + "incompleteColumns": Object {}, + }, + "ec84ba70-2adb-4647-8ef0-8ad91a0e6d4e": Object { + "columnOrder": Array [ + "c72aad6a-fc9c-43dc-9194-e13ca3ee8aff", + "b7e59b08-96e6-40d1-84fd-e97b977d1c47", + ], + "columns": Object { + "b7e59b08-96e6-40d1-84fd-e97b977d1c47": Object { + "dataType": "number", + "isBucketed": false, + "label": "Unique count of destination.ip", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + "c72aad6a-fc9c-43dc-9194-e13ca3ee8aff": Object { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Dest.", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "", + }, + "label": "Dest.", + }, + ], + }, + "scale": "ordinal", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "32f66676-f4e1-48fd-b7f8-d4de38318601", + ], + "layerId": "8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "layerType": "data", + "seriesType": "bar_horizontal_stacked", + "xAccessor": "f8bfa719-5c1c-4bf2-896e-c318d77fc08e", + "yConfig": Array [ + Object { + "color": "#d36186", + "forAccessor": "32f66676-f4e1-48fd-b7f8-d4de38318601", + }, + ], + }, + Object { + "accessors": Array [ + "b7e59b08-96e6-40d1-84fd-e97b977d1c47", + ], + "layerId": "ec84ba70-2adb-4647-8ef0-8ad91a0e6d4e", + "layerType": "data", + "seriesType": "bar_horizontal_stacked", + "xAccessor": "c72aad6a-fc9c-43dc-9194-e13ca3ee8aff", + "yConfig": Array [ + Object { + "color": "#9170b8", + "forAccessor": "b7e59b08-96e6-40d1-84fd-e97b977d1c47", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "bar_horizontal_stacked", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Host] Unique IPs - bar", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap new file mode 100644 index 000000000000..ebeb85e27a44 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap @@ -0,0 +1,143 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniqueIpsDestinationMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "8be0156b-d423-4a39-adf1-f54d4c9f2e69": Object { + "columnOrder": Array [ + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + ], + "columns": Object { + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + "layerId": "8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "layerType": "data", + }, + }, + "title": "[Host] Unique IPs - destination metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap new file mode 100644 index 000000000000..f8ec7bb8c70d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap @@ -0,0 +1,143 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniqueIpsSourceMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "8be0156b-d423-4a39-adf1-f54d4c9f2e69": Object { + "columnOrder": Array [ + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + ], + "columns": Object { + "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.name", + "negate": false, + "params": Object { + "query": "mockHost", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.name": "mockHost", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "host.name", + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "d9a6eb6b-8b78-439e-98e7-a718f8ffbebe", + "layerId": "8be0156b-d423-4a39-adf1-f54d4c9f2e69", + "layerType": "data", + }, + }, + "title": "[Host] Unique IPs - source metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/event.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/event.test.ts new file mode 100644 index 000000000000..107b63716f40 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/event.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { getEventsHistogramLensAttributes } from './events'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('getEventsHistogramLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.test.ts new file mode 100644 index 000000000000..524844487294 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiHostAreaLensAttributes } from './kpi_host_area'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiHostAreaLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiHostAreaLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.ts index f4486b77390b..64f62133e940 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_area.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { UNIQUE_COUNT } from '../../translations'; import type { LensAttributes } from '../../types'; export const kpiHostAreaLensAttributes: LensAttributes = { @@ -32,7 +33,7 @@ export const kpiHostAreaLensAttributes: LensAttributes = { customLabel: true, dataType: 'number', isBucketed: false, - label: ' ', + label: UNIQUE_COUNT('host.name'), operationType: 'unique_count', scale: 'ratio', sourceField: 'host.name', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.test.ts new file mode 100644 index 000000000000..06a884c15e4d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiHostMetricLensAttributes } from './kpi_host_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiHostMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiHostMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.ts index c5fcae45df0f..00ab0239acb4 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric.ts @@ -40,7 +40,7 @@ export const kpiHostMetricLensAttributes: LensAttributes = { }, }, title: '[Host] Hosts - metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_area.test.ts new file mode 100644 index 000000000000..63f50b141b5b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_area.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniqueIpsAreaLensAttributes } from './kpi_unique_ips_area'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniqueIpsAreaLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniqueIpsAreaLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.test.ts new file mode 100644 index 000000000000..f04f0de2b8be --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniqueIpsBarLensAttributes } from './kpi_unique_ips_bar'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniqueIpsBarLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniqueIpsBarLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.ts index b55fcb64ba49..cf7dbf21913b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar.ts @@ -6,7 +6,7 @@ */ import type { LensAttributes } from '../../types'; -import { SOURCE_CHART_LABEL, DESTINATION_CHART_LABEL } from '../../translations'; +import { SOURCE_CHART_LABEL, DESTINATION_CHART_LABEL, UNIQUE_COUNT } from '../../translations'; export const kpiUniqueIpsBarLensAttributes: LensAttributes = { description: '', @@ -23,7 +23,7 @@ export const kpiUniqueIpsBarLensAttributes: LensAttributes = { '32f66676-f4e1-48fd-b7f8-d4de38318601': { dataType: 'number', isBucketed: false, - label: 'Unique count of source.ip', + label: UNIQUE_COUNT('source.ip'), operationType: 'unique_count', scale: 'ratio', sourceField: 'source.ip', @@ -50,7 +50,7 @@ export const kpiUniqueIpsBarLensAttributes: LensAttributes = { 'b7e59b08-96e6-40d1-84fd-e97b977d1c47': { dataType: 'number', isBucketed: false, - label: 'Unique count of destination.ip', + label: UNIQUE_COUNT('destination.ip'), operationType: 'unique_count', scale: 'ratio', sourceField: 'destination.ip', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.test.ts new file mode 100644 index 000000000000..9dd67a2d5ab4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniqueIpsDestinationMetricLensAttributes } from './kpi_unique_ips_destination_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniqueIpsDestinationMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniqueIpsDestinationMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.ts index ae18c08be800..5c4aa31f6583 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric.ts @@ -40,7 +40,7 @@ export const kpiUniqueIpsDestinationMetricLensAttributes: LensAttributes = { }, }, title: '[Host] Unique IPs - destination metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.test.ts new file mode 100644 index 000000000000..6e69495e63a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniqueIpsSourceMetricLensAttributes } from './kpi_unique_ips_source_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'hosts', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniqueIpsSourceMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniqueIpsSourceMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.ts index 8a0b778975ab..4d308b95d796 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric.ts @@ -40,7 +40,7 @@ export const kpiUniqueIpsSourceMetricLensAttributes: LensAttributes = { }, }, title: '[Host] Unique IPs - source metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap new file mode 100644 index 000000000000..6e0f9c2bbd51 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap @@ -0,0 +1,245 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`dnsTopDomainsLensAttributes should render 1`] = ` +Object { + "description": "Security Solution Network DNS", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-b1c3efc6-c886-4fba-978f-3b6bb5e7948a", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "filter-index-pattern-0", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "b1c3efc6-c886-4fba-978f-3b6bb5e7948a": Object { + "columnOrder": Array [ + "e8842815-2a45-4c74-86de-c19a391e2424", + "d1452b87-0e9e-4fc0-a725-3727a18e0b37", + "2a4d5e20-f570-48e4-b9ab-ff3068919377", + ], + "columns": Object { + "2a4d5e20-f570-48e4-b9ab-ff3068919377": Object { + "dataType": "number", + "isBucketed": false, + "label": "Unique count of dns.question.registered_domain", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "dns.question.registered_domain", + }, + "d1452b87-0e9e-4fc0-a725-3727a18e0b37": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "e8842815-2a45-4c74-86de-c19a391e2424": Object { + "dataType": "string", + "isBucketed": true, + "label": "Top values of dns.question.name", + "operationType": "terms", + "params": Object { + "missingBucket": false, + "orderBy": Object { + "columnId": "2a4d5e20-f570-48e4-b9ab-ff3068919377", + "type": "column", + }, + "orderDirection": "desc", + "otherBucket": true, + "size": 6, + }, + "scale": "ordinal", + "sourceField": "dns.question.name", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "indexRefName": "filter-index-pattern-0", + "key": "dns.question.type", + "negate": true, + "params": Object { + "query": "PTR", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "dns.question.type": "PTR", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "2a4d5e20-f570-48e4-b9ab-ff3068919377", + ], + "layerId": "b1c3efc6-c886-4fba-978f-3b6bb5e7948a", + "layerType": "data", + "position": "top", + "seriesType": "bar", + "showGridlines": false, + "splitAccessor": "e8842815-2a45-4c74-86de-c19a391e2424", + "xAccessor": "d1452b87-0e9e-4fc0-a725-3727a18e0b37", + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "bar", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "Top domains by dns.question.registered_domain", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap new file mode 100644 index 000000000000..39f16779abaf --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap @@ -0,0 +1,189 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiDnsQueriesLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "196d783b-3779-4c39-898e-6606fe633d05", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "cea37c70-8f91-43bf-b9fe-72d8c049f6a3": Object { + "columnOrder": Array [ + "0374e520-eae0-4ac1-bcfe-37565e7fc9e3", + ], + "columns": Object { + "0374e520-eae0-4ac1-bcfe-37565e7fc9e3": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "196d783b-3779-4c39-898e-6606fe633d05", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"dns.question.name\\"}},{\\"term\\":{\\"suricata.eve.dns.type\\":{\\"value\\":\\"query\\"}}},{\\"exists\\":{\\"field\\":\\"zeek.dns.query\\"}}],\\"minimum_should_match\\":1}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "dns.question.name", + }, + }, + Object { + "term": Object { + "suricata.eve.dns.type": Object { + "value": "query", + }, + }, + }, + Object { + "exists": Object { + "field": "zeek.dns.query", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "0374e520-eae0-4ac1-bcfe-37565e7fc9e3", + "colorMode": "None", + "layerId": "cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "layerType": "data", + }, + }, + "title": "[Network] DNS metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap new file mode 100644 index 000000000000..03bacfac49ad --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap @@ -0,0 +1,193 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiNetworkEventsLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-eaadfec7-deaa-4aeb-a403-3b4e516416d2", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "861af17d-be25-45a3-a82d-d6e697b76e51", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "09617767-f732-410e-af53-bebcbd0bf4b9", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "eaadfec7-deaa-4aeb-a403-3b4e516416d2": Object { + "columnOrder": Array [ + "370ebd07-5ce0-4f46-a847-0e363c50d037", + ], + "columns": Object { + "370ebd07-5ce0-4f46-a847-0e363c50d037": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "security-solution-default", + "key": "source.ip", + "negate": false, + "type": "exists", + "value": "exists", + }, + "query": Object { + "exists": Object { + "field": "source.ip", + }, + }, + }, + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "security-solution-default", + "key": "destination.ip", + "negate": false, + "type": "exists", + "value": "exists", + }, + "query": Object { + "exists": Object { + "field": "destination.ip", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "370ebd07-5ce0-4f46-a847-0e363c50d037", + "layerId": "eaadfec7-deaa-4aeb-a403-3b4e516416d2", + "layerType": "data", + }, + }, + "title": "[Network] Network events", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap new file mode 100644 index 000000000000..6e695484fdc0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap @@ -0,0 +1,212 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiTlsHandshakesLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-1f48a633-8eee-45ae-9471-861227e9ca03", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "1f48a633-8eee-45ae-9471-861227e9ca03": Object { + "columnOrder": Array [ + "21052b6b-5504-4084-a2e2-c17f772345cf", + ], + "columns": Object { + "21052b6b-5504-4084-a2e2-c17f772345cf": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "32ee22d9-2e77-4aee-8073-87750e92c3ee", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"source.ip\\"}},{\\"exists\\":{\\"field\\":\\"destination.ip\\"}}],\\"minimum_should_match\\":1}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + }, + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "1e93f984-9374-4755-a198-de57751533c6", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"tls.version\\"}},{\\"exists\\":{\\"field\\":\\"suricata.eve.tls.version\\"}},{\\"exists\\":{\\"field\\":\\"zeek.ssl.version\\"}}],\\"minimum_should_match\\":1}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "tls.version", + }, + }, + Object { + "exists": Object { + "field": "suricata.eve.tls.version", + }, + }, + Object { + "exists": Object { + "field": "zeek.ssl.version", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "21052b6b-5504-4084-a2e2-c17f772345cf", + "layerId": "1f48a633-8eee-45ae-9471-861227e9ca03", + "layerType": "data", + }, + }, + "title": "[Network] TLS handshakes", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap new file mode 100644 index 000000000000..1e3f1f63c40c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap @@ -0,0 +1,176 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniqueFlowIdsLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-5d46d48f-6ce8-46be-a797-17ad50642564", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "5d46d48f-6ce8-46be-a797-17ad50642564": Object { + "columnOrder": Array [ + "a27f3503-9c73-4fc1-86bb-12461dae4b70", + ], + "columns": Object { + "a27f3503-9c73-4fc1-86bb-12461dae4b70": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "network.community_id", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "c01edc8a-90ce-4d49-95f0-76954a034eb2", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"source.ip\\"}},{\\"exists\\":{\\"field\\":\\"destination.ip\\"}}],\\"minimum_should_match\\":1}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "a27f3503-9c73-4fc1-86bb-12461dae4b70", + "layerId": "5d46d48f-6ce8-46be-a797-17ad50642564", + "layerType": "data", + }, + }, + "title": "[Network] Unique flow IDs", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap new file mode 100644 index 000000000000..2415dcc6c750 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap @@ -0,0 +1,261 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniquePrivateIpsAreaLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-72dc4b99-b07d-4dc9-958b-081d259e11fa", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7": Object { + "columnOrder": Array [ + "662cd5e5-82bf-4325-a703-273f84b97e09", + "5f317308-cfbb-4ee5-bfb9-07653184fabf", + ], + "columns": Object { + "5f317308-cfbb-4ee5-bfb9-07653184fabf": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "\\"source.ip\\": \\"10.0.0.0/8\\" or \\"source.ip\\": \\"192.168.0.0/16\\" or \\"source.ip\\": \\"172.16.0.0/12\\" or \\"source.ip\\": \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "Src.", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + "662cd5e5-82bf-4325-a703-273f84b97e09": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + "72dc4b99-b07d-4dc9-958b-081d259e11fa": Object { + "columnOrder": Array [ + "36444b8c-7e10-4069-8298-6c1b46912be2", + "ac1eb80c-ddde-46c4-a90c-400261926762", + ], + "columns": Object { + "36444b8c-7e10-4069-8298-6c1b46912be2": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "ac1eb80c-ddde-46c4-a90c-400261926762": Object { + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "\\"destination.ip\\": \\"10.0.0.0/8\\" or \\"destination.ip\\": \\"192.168.0.0/16\\" or \\"destination.ip\\": \\"172.16.0.0/12\\" or \\"destination.ip\\": \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "Dest.", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "5f317308-cfbb-4ee5-bfb9-07653184fabf", + ], + "layerId": "38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7", + "layerType": "data", + "seriesType": "area", + "xAccessor": "662cd5e5-82bf-4325-a703-273f84b97e09", + "yConfig": Array [ + Object { + "color": "#d36186", + "forAccessor": "5f317308-cfbb-4ee5-bfb9-07653184fabf", + }, + ], + }, + Object { + "accessors": Array [ + "ac1eb80c-ddde-46c4-a90c-400261926762", + ], + "layerId": "72dc4b99-b07d-4dc9-958b-081d259e11fa", + "layerType": "data", + "seriesType": "area", + "xAccessor": "36444b8c-7e10-4069-8298-6c1b46912be2", + "yConfig": Array [ + Object { + "color": "#9170b8", + "forAccessor": "ac1eb80c-ddde-46c4-a90c-400261926762", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "area", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Network] Unique private IPs - area chart", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap new file mode 100644 index 000000000000..2ea658869183 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap @@ -0,0 +1,276 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniquePrivateIpsBarLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-e406bf4f-942b-41ac-b516-edb5cef06ec8", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7": Object { + "columnOrder": Array [ + "4607c585-3af3-43b9-804f-e49b27796d79", + "d27e0966-daf9-41f4-9033-230cf1e76dc9", + ], + "columns": Object { + "4607c585-3af3-43b9-804f-e49b27796d79": Object { + "dataType": "string", + "isBucketed": true, + "label": "Filters", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "", + }, + "label": "Dest.", + }, + ], + }, + "scale": "ordinal", + }, + "d27e0966-daf9-41f4-9033-230cf1e76dc9": Object { + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "\\"destination.ip\\": \\"10.0.0.0/8\\" or \\"destination.ip\\": \\"192.168.0.0/16\\" or \\"destination.ip\\": \\"172.16.0.0/12\\" or \\"destination.ip\\": \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "Unique count of destination.ip", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + }, + "incompleteColumns": Object {}, + }, + "e406bf4f-942b-41ac-b516-edb5cef06ec8": Object { + "columnOrder": Array [ + "d9c438c5-f776-4436-9d20-d62dc8c03be8", + "5acd4c9d-dc3b-4b21-9632-e4407944c36d", + ], + "columns": Object { + "5acd4c9d-dc3b-4b21-9632-e4407944c36d": Object { + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "source.ip: \\"10.0.0.0/8\\" or source.ip: \\"192.168.0.0/16\\" or source.ip: \\"172.16.0.0/12\\" or source.ip: \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "Unique count of source.ip", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + "d9c438c5-f776-4436-9d20-d62dc8c03be8": Object { + "dataType": "string", + "isBucketed": true, + "label": "Filters", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "", + }, + "label": "Src.", + }, + ], + }, + "scale": "ordinal", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "5acd4c9d-dc3b-4b21-9632-e4407944c36d", + ], + "layerId": "e406bf4f-942b-41ac-b516-edb5cef06ec8", + "layerType": "data", + "position": "top", + "seriesType": "bar_horizontal_stacked", + "showGridlines": false, + "xAccessor": "d9c438c5-f776-4436-9d20-d62dc8c03be8", + "yConfig": Array [ + Object { + "color": "#d36186", + "forAccessor": "5acd4c9d-dc3b-4b21-9632-e4407944c36d", + }, + ], + }, + Object { + "accessors": Array [ + "d27e0966-daf9-41f4-9033-230cf1e76dc9", + ], + "layerId": "38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7", + "layerType": "data", + "seriesType": "bar_horizontal_stacked", + "xAccessor": "4607c585-3af3-43b9-804f-e49b27796d79", + "yConfig": Array [ + Object { + "color": "#9170b8", + "forAccessor": "d27e0966-daf9-41f4-9033-230cf1e76dc9", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "bar_horizontal_stacked", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Network] Unique private IPs - bar chart", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap new file mode 100644 index 000000000000..37311a980c6b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap @@ -0,0 +1,149 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniquePrivateIpsDestinationMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "cea37c70-8f91-43bf-b9fe-72d8c049f6a3": Object { + "columnOrder": Array [ + "bd17c23e-4f83-4108-8005-2669170d064b", + ], + "columns": Object { + "bd17c23e-4f83-4108-8005-2669170d064b": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "\\"destination.ip\\": \\"10.0.0.0/8\\" or \\"destination.ip\\": \\"192.168.0.0/16\\" or \\"destination.ip\\": \\"172.16.0.0/12\\" or \\"destination.ip\\": \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "destination.ip", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "bd17c23e-4f83-4108-8005-2669170d064b", + "layerId": "cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "layerType": "data", + }, + }, + "title": "[Network] Unique private IPs - destination metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap new file mode 100644 index 000000000000..2f7ba7d2997b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap @@ -0,0 +1,149 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUniquePrivateIpsSourceMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "cea37c70-8f91-43bf-b9fe-72d8c049f6a3": Object { + "columnOrder": Array [ + "bd17c23e-4f83-4108-8005-2669170d064b", + ], + "columns": Object { + "bd17c23e-4f83-4108-8005-2669170d064b": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "source.ip: \\"10.0.0.0/8\\" or source.ip: \\"192.168.0.0/16\\" or source.ip: \\"172.16.0.0/12\\" or source.ip: \\"fd00::/8\\"", + }, + "isBucketed": false, + "label": "", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "source.ip", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"source.ip\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\": \\"destination.ip\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "source.ip", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "destination.ip", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "bd17c23e-4f83-4108-8005-2669170d064b", + "layerId": "cea37c70-8f91-43bf-b9fe-72d8c049f6a3", + "layerType": "data", + }, + }, + "title": "[Network] Unique private IPs - source metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.test.ts new file mode 100644 index 000000000000..a726a44e34e3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { dnsTopDomainsLensAttributes } from './dns_top_domains'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('dnsTopDomainsLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: dnsTopDomainsLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts index 07a3badc3b96..ef75bea77c3e 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { TOP_VALUE, UNIQUE_COUNT } from '../../translations'; import type { LensAttributes } from '../../types'; /* Exported from Kibana Saved Object */ @@ -104,7 +105,7 @@ export const dnsTopDomainsLensAttributes: LensAttributes = { }, }, '2a4d5e20-f570-48e4-b9ab-ff3068919377': { - label: 'Unique count of dns.question.registered_domain', + label: UNIQUE_COUNT('dns.question.registered_domain'), dataType: 'number', operationType: 'unique_count', scale: 'ratio', @@ -112,7 +113,7 @@ export const dnsTopDomainsLensAttributes: LensAttributes = { isBucketed: false, }, 'e8842815-2a45-4c74-86de-c19a391e2424': { - label: 'Top values of dns.question.name', + label: TOP_VALUE('dns.question.name'), dataType: 'string', operationType: 'terms', scale: 'ordinal', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.test.ts new file mode 100644 index 000000000000..b19b5c1f2f1b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiDnsQueriesLensAttributes } from './kpi_dns_queries'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiDnsQueriesLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiDnsQueriesLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.ts index 1d6cddb7f1b6..681cd278214b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_dns_queries.ts @@ -10,7 +10,7 @@ import type { LensAttributes } from '../../types'; export const kpiDnsQueriesLensAttributes: LensAttributes = { title: '[Network] DNS metric', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { layerId: 'cea37c70-8f91-43bf-b9fe-72d8c049f6a3', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.test.ts new file mode 100644 index 000000000000..4ca26f222021 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiNetworkEventsLensAttributes } from './kpi_network_events'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiNetworkEventsLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiNetworkEventsLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.ts index 013ad35b31ec..534ffeb2024e 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_network_events.ts @@ -10,7 +10,7 @@ import type { LensAttributes } from '../../types'; export const kpiNetworkEventsLensAttributes: LensAttributes = { title: '[Network] Network events', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { layerId: 'eaadfec7-deaa-4aeb-a403-3b4e516416d2', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.test.ts new file mode 100644 index 000000000000..f06f478ca0e2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiTlsHandshakesLensAttributes } from './kpi_tls_handshakes'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiTlsHandshakesLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiTlsHandshakesLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.ts index 343c61dbd2be..367fe6fd40f6 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes.ts @@ -9,7 +9,7 @@ import type { LensAttributes } from '../../types'; export const kpiTlsHandshakesLensAttributes: LensAttributes = { title: '[Network] TLS handshakes', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { layerId: '1f48a633-8eee-45ae-9471-861227e9ca03', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.test.ts new file mode 100644 index 000000000000..64b9be02a1d1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniqueFlowIdsLensAttributes } from './kpi_unique_flow_ids'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniqueFlowIdsLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniqueFlowIdsLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.ts index 3646e3c0a70b..5f31645c75ec 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids.ts @@ -10,7 +10,7 @@ import type { LensAttributes } from '../../types'; export const kpiUniqueFlowIdsLensAttributes: LensAttributes = { title: '[Network] Unique flow IDs', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { layerId: '5d46d48f-6ce8-46be-a797-17ad50642564', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.test.ts new file mode 100644 index 000000000000..8bb98ddaf95c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniquePrivateIpsAreaLensAttributes } from './kpi_unique_private_ips_area'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniquePrivateIpsAreaLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniquePrivateIpsAreaLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.ts index 2d3792e39937..394bc227e871 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { DESTINATION_CHART_LABEL, SOURCE_CHART_LABEL } from '../../translations'; import type { LensAttributes } from '../../types'; export const kpiUniquePrivateIpsAreaLensAttributes: LensAttributes = { @@ -97,7 +98,7 @@ export const kpiUniquePrivateIpsAreaLensAttributes: LensAttributes = { }, }, '5f317308-cfbb-4ee5-bfb9-07653184fabf': { - label: 'Src.', + label: SOURCE_CHART_LABEL, dataType: 'number', operationType: 'unique_count', scale: 'ratio', @@ -131,7 +132,7 @@ export const kpiUniquePrivateIpsAreaLensAttributes: LensAttributes = { }, }, 'ac1eb80c-ddde-46c4-a90c-400261926762': { - label: 'Dest.', + label: DESTINATION_CHART_LABEL, dataType: 'number', operationType: 'unique_count', scale: 'ratio', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.test.ts new file mode 100644 index 000000000000..894144d383b5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniquePrivateIpsBarLensAttributes } from './kpi_unique_private_ips_bar'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniquePrivateIpsBarLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniquePrivateIpsBarLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.ts index bf4ad0e70408..fe4a698aedf5 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SOURCE_CHART_LABEL, DESTINATION_CHART_LABEL } from '../../translations'; +import { SOURCE_CHART_LABEL, DESTINATION_CHART_LABEL, UNIQUE_COUNT } from '../../translations'; import type { LensAttributes } from '../../types'; export const kpiUniquePrivateIpsBarLensAttributes: LensAttributes = { @@ -90,7 +90,7 @@ export const kpiUniquePrivateIpsBarLensAttributes: LensAttributes = { 'e406bf4f-942b-41ac-b516-edb5cef06ec8': { columns: { '5acd4c9d-dc3b-4b21-9632-e4407944c36d': { - label: SOURCE_CHART_LABEL, + label: UNIQUE_COUNT('source.ip'), dataType: 'number', isBucketed: false, operationType: 'unique_count', @@ -130,7 +130,7 @@ export const kpiUniquePrivateIpsBarLensAttributes: LensAttributes = { '38aa6532-6bf9-4c8f-b2a6-da8d32f7d0d7': { columns: { 'd27e0966-daf9-41f4-9033-230cf1e76dc9': { - label: DESTINATION_CHART_LABEL, + label: UNIQUE_COUNT('destination.ip'), dataType: 'number', isBucketed: false, operationType: 'unique_count', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.test.ts new file mode 100644 index 000000000000..7d65e042554b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniquePrivateIpsDestinationMetricLensAttributes } from './kpi_unique_private_ips_destination_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniquePrivateIpsDestinationMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniquePrivateIpsDestinationMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.ts index a2bccef3b624..6e3d440619e7 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric.ts @@ -48,7 +48,7 @@ export const kpiUniquePrivateIpsDestinationMetricLensAttributes: LensAttributes }, }, title: '[Network] Unique private IPs - destination metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.test.ts new file mode 100644 index 000000000000..88042d866325 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUniquePrivateIpsSourceMetricLensAttributes } from './kpi_unique_private_ips_source_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'network', + tabName: 'events', + }, + ]), +})); + +describe('kpiUniquePrivateIpsSourceMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUniquePrivateIpsSourceMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.ts index a95745c7b96e..3f1110d70630 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric.ts @@ -47,7 +47,7 @@ export const kpiUniquePrivateIpsSourceMetricLensAttributes: LensAttributes = { }, }, title: '[Network] Unique private IPs - source metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap new file mode 100644 index 000000000000..11df964f2eca --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap @@ -0,0 +1,151 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiTotalUsersAreaLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-416b6fad-1923-4f6a-a2df-b223bb287e30", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "416b6fad-1923-4f6a-a2df-b223bb287e30": Object { + "columnOrder": Array [ + "5eea817b-67b7-4268-8ecb-7688d1094721", + "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + ], + "columns": Object { + "5eea817b-67b7-4268-8ecb-7688d1094721": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + "b00c65ea-32be-4163-bfc8-f795b1ef9d06": Object { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Unique count of user.name", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "user.name", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": false, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "b00c65ea-32be-4163-bfc8-f795b1ef9d06", + ], + "layerId": "416b6fad-1923-4f6a-a2df-b223bb287e30", + "layerType": "data", + "seriesType": "area", + "xAccessor": "5eea817b-67b7-4268-8ecb-7688d1094721", + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "area", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[User] Users - area", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap new file mode 100644 index 000000000000..b53e1bd24d30 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap @@ -0,0 +1,97 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiTotalUsersMetricLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-416b6fad-1923-4f6a-a2df-b223bb287e30", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "416b6fad-1923-4f6a-a2df-b223bb287e30": Object { + "columnOrder": Array [ + "3e51b035-872c-4b44-824b-fe069c222e91", + ], + "columns": Object { + "3e51b035-872c-4b44-824b-fe069c222e91": Object { + "dataType": "number", + "isBucketed": false, + "label": " ", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "user.name", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "3e51b035-872c-4b44-824b-fe069c222e91", + "layerId": "416b6fad-1923-4f6a-a2df-b223bb287e30", + "layerType": "data", + }, + }, + "title": "[User] Users - metric", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap new file mode 100644 index 000000000000..37c20b7e8026 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUserAuthenticationsMetricFailureLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "4590dafb-4ac7-45aa-8641-47a3ff0b817c": Object { + "columnOrder": Array [ + "0eb97c09-a351-4280-97da-944e4bd30dd7", + ], + "columns": Object { + "0eb97c09-a351-4280-97da-944e4bd30dd7": Object { + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome : \\"failure\\" ", + }, + "isBucketed": false, + "label": "", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "indexRefName": "filter-index-pattern-0", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"term\\":{\\"event.category\\":\\"authentication\\"}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "event.category": "authentication", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "0eb97c09-a351-4280-97da-944e4bd30dd7", + "layerId": "4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "layerType": "data", + }, + }, + "title": "[Host] User authentications - metric failure ", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap new file mode 100644 index 000000000000..1954bccfaffb --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap @@ -0,0 +1,240 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUserAuthenticationsAreaLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "{dataViewId}", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-31213ae3-905b-4e88-b987-0cccb1f3209f", + "type": "{dataViewId}", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "type": "{dataViewId}", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "31213ae3-905b-4e88-b987-0cccb1f3209f": Object { + "columnOrder": Array [ + "33a6163d-0c0a-451d-aa38-8ca6010dd5bf", + "2b27c80e-a20d-46f1-8fb2-79626ef4563c", + ], + "columns": Object { + "2b27c80e-a20d-46f1-8fb2-79626ef4563c": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome: \\"failure\\" ", + }, + "isBucketed": false, + "label": "Fail", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "33a6163d-0c0a-451d-aa38-8ca6010dd5bf": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + "4590dafb-4ac7-45aa-8641-47a3ff0b817c": Object { + "columnOrder": Array [ + "49a42fe6-ebe8-4adb-8eed-1966a5297b7e", + "0eb97c09-a351-4280-97da-944e4bd30dd7", + ], + "columns": Object { + "0eb97c09-a351-4280-97da-944e4bd30dd7": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome : \\"success\\" ", + }, + "isBucketed": false, + "label": "Succ.", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "49a42fe6-ebe8-4adb-8eed-1966a5297b7e": Object { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": Object { + "interval": "auto", + }, + "scale": "interval", + "sourceField": "@timestamp", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "indexRefName": "filter-index-pattern-0", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"term\\":{\\"event.category\\":\\"authentication\\"}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "event.category": "authentication", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": true, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "0eb97c09-a351-4280-97da-944e4bd30dd7", + ], + "layerId": "4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "layerType": "data", + "seriesType": "area", + "xAccessor": "49a42fe6-ebe8-4adb-8eed-1966a5297b7e", + "yConfig": Array [ + Object { + "color": "#54b399", + "forAccessor": "0eb97c09-a351-4280-97da-944e4bd30dd7", + }, + ], + }, + Object { + "accessors": Array [ + "2b27c80e-a20d-46f1-8fb2-79626ef4563c", + ], + "layerId": "31213ae3-905b-4e88-b987-0cccb1f3209f", + "layerType": "data", + "seriesType": "area", + "xAccessor": "33a6163d-0c0a-451d-aa38-8ca6010dd5bf", + "yConfig": Array [ + Object { + "color": "#e7664c", + "forAccessor": "2b27c80e-a20d-46f1-8fb2-79626ef4563c", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "area", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Host] User authentications - area ", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap new file mode 100644 index 000000000000..5335dca6057a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap @@ -0,0 +1,240 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUserAuthenticationsBarLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-31213ae3-905b-4e88-b987-0cccb1f3209f", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-b9acd453-f476-4467-ad38-203e37b73e55", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "31213ae3-905b-4e88-b987-0cccb1f3209f": Object { + "columnOrder": Array [ + "430e690c-9992-414f-9bce-00812d99a5e7", + "938b445a-a291-4bbc-84fe-4f47b69c20e4", + ], + "columns": Object { + "430e690c-9992-414f-9bce-00812d99a5e7": Object { + "dataType": "string", + "isBucketed": true, + "label": "Filters", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "event.outcome : \\"success\\" ", + }, + "label": "Succ.", + }, + ], + }, + "scale": "ordinal", + }, + "938b445a-a291-4bbc-84fe-4f47b69c20e4": Object { + "dataType": "number", + "isBucketed": false, + "label": "Succ.", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + "b9acd453-f476-4467-ad38-203e37b73e55": Object { + "columnOrder": Array [ + "e959c351-a3a2-4525-b244-9623f215a8fd", + "c8165fc3-7180-4f1b-8c87-bc3ea04c6df7", + ], + "columns": Object { + "c8165fc3-7180-4f1b-8c87-bc3ea04c6df7": Object { + "dataType": "number", + "isBucketed": false, + "label": "Fail", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + "e959c351-a3a2-4525-b244-9623f215a8fd": Object { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Fail", + "operationType": "filters", + "params": Object { + "filters": Array [ + Object { + "input": Object { + "language": "kuery", + "query": "event.outcome:\\"failure\\" ", + }, + "label": "Fail", + }, + ], + }, + "scale": "ordinal", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "indexRefName": "filter-index-pattern-0", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"term\\":{\\"event.category\\":\\"authentication\\"}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "event.category": "authentication", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "axisTitlesVisibilitySettings": Object { + "x": false, + "yLeft": false, + "yRight": true, + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "labelsOrientation": Object { + "x": 0, + "yLeft": 0, + "yRight": 0, + }, + "layers": Array [ + Object { + "accessors": Array [ + "938b445a-a291-4bbc-84fe-4f47b69c20e4", + ], + "layerId": "31213ae3-905b-4e88-b987-0cccb1f3209f", + "layerType": "data", + "seriesType": "bar_horizontal_stacked", + "xAccessor": "430e690c-9992-414f-9bce-00812d99a5e7", + "yConfig": Array [], + }, + Object { + "accessors": Array [ + "c8165fc3-7180-4f1b-8c87-bc3ea04c6df7", + ], + "layerId": "b9acd453-f476-4467-ad38-203e37b73e55", + "layerType": "data", + "seriesType": "bar_horizontal_stacked", + "xAccessor": "e959c351-a3a2-4525-b244-9623f215a8fd", + "yConfig": Array [ + Object { + "color": "#e7664c", + "forAccessor": "c8165fc3-7180-4f1b-8c87-bc3ea04c6df7", + }, + ], + }, + ], + "legend": Object { + "isVisible": false, + "position": "right", + "showSingleSeries": false, + }, + "preferredSeriesType": "bar_horizontal_stacked", + "tickLabelsVisibilitySettings": Object { + "x": true, + "yLeft": true, + "yRight": true, + }, + "valueLabels": "hide", + "yLeftExtent": Object { + "mode": "full", + }, + "yRightExtent": Object { + "mode": "full", + }, + }, + }, + "title": "[Host] User authentications - bar ", + "visualizationType": "lnsXY", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap new file mode 100644 index 000000000000..4cadcaf19e91 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap @@ -0,0 +1,127 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`kpiUserAuthenticationsMetricSuccessLensAttributes should render 1`] = ` +Object { + "description": "", + "references": Array [ + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern", + }, + Object { + "id": "security-solution-my-test", + "name": "indexpattern-datasource-layer-4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "type": "index-pattern", + }, + ], + "state": Object { + "datasourceStates": Object { + "indexpattern": Object { + "layers": Object { + "4590dafb-4ac7-45aa-8641-47a3ff0b817c": Object { + "columnOrder": Array [ + "0eb97c09-a351-4280-97da-944e4bd30dd7", + ], + "columns": Object { + "0eb97c09-a351-4280-97da-944e4bd30dd7": Object { + "customLabel": true, + "dataType": "number", + "filter": Object { + "language": "kuery", + "query": "event.outcome : \\"success\\" ", + }, + "isBucketed": false, + "label": " ", + "operationType": "count", + "scale": "ratio", + "sourceField": "___records___", + }, + }, + "incompleteColumns": Object {}, + }, + }, + }, + }, + "filters": Array [ + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "indexRefName": "filter-index-pattern-0", + "key": "query", + "negate": false, + "type": "custom", + "value": "{\\"bool\\":{\\"filter\\":[{\\"term\\":{\\"event.category\\":\\"authentication\\"}}]}}", + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "event.category": "authentication", + }, + }, + ], + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "_index", + "negate": false, + "params": Array [ + "auditbeat-mytest-*", + ], + "type": "phrases", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "_index": "auditbeat-mytest-*", + }, + }, + ], + }, + }, + }, + ], + "query": Object { + "language": "kql", + "query": "host.name: *", + }, + "visualization": Object { + "accessor": "0eb97c09-a351-4280-97da-944e4bd30dd7", + "layerId": "4590dafb-4ac7-45aa-8641-47a3ff0b817c", + "layerType": "data", + }, + }, + "title": "[Host] User authentications - metric success ", + "visualizationType": "lnsLegacyMetric", +} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.test.ts new file mode 100644 index 000000000000..43cb5be3a973 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiTotalUsersAreaLensAttributes } from './kpi_total_users_area'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiTotalUsersAreaLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiTotalUsersAreaLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.ts index d958f9304b8a..c97748077a6b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_area.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { UNIQUE_COUNT } from '../../translations'; import type { LensAttributes } from '../../types'; export const kpiTotalUsersAreaLensAttributes: LensAttributes = { @@ -32,7 +33,7 @@ export const kpiTotalUsersAreaLensAttributes: LensAttributes = { customLabel: true, dataType: 'number', isBucketed: false, - label: ' ', + label: UNIQUE_COUNT('user.name'), operationType: 'unique_count', scale: 'ratio', sourceField: 'user.name', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.test.ts new file mode 100644 index 000000000000..0a5ec9891d26 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiTotalUsersMetricLensAttributes } from './kpi_total_users_metric'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiTotalUsersMetricLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiTotalUsersMetricLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.ts index 08e5756337dc..faa6b62e18b6 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric.ts @@ -19,7 +19,7 @@ export const kpiTotalUsersMetricLensAttributes: LensAttributes = { '3e51b035-872c-4b44-824b-fe069c222e91': { dataType: 'number', isBucketed: false, - label: 'Unique count of user.name', + label: ' ', operationType: 'unique_count', scale: 'ratio', sourceField: 'user.name', @@ -39,7 +39,7 @@ export const kpiTotalUsersMetricLensAttributes: LensAttributes = { }, }, title: '[User] Users - metric', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', references: [ { id: '{dataViewId}', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.test.ts new file mode 100644 index 000000000000..e251ccf51c5e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUserAuthenticationsMetricFailureLensAttributes } from './kpi_user_authentication_metric_failure'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiUserAuthenticationsMetricFailureLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUserAuthenticationsMetricFailureLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.ts index e4690f66998c..3f421f8a1c30 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure.ts @@ -10,7 +10,7 @@ import type { LensAttributes } from '../../types'; export const kpiUserAuthenticationsMetricFailureLensAttributes: LensAttributes = { title: '[Host] User authentications - metric failure ', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { accessor: '0eb97c09-a351-4280-97da-944e4bd30dd7', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.test.ts new file mode 100644 index 000000000000..b51d3e474a70 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUserAuthenticationsAreaLensAttributes } from './kpi_user_authentications_area'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiUserAuthenticationsAreaLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUserAuthenticationsAreaLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.ts index cf9902bb2413..d96ea21489bb 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { FAIL_CHART_LABEL, SUCCESS_CHART_LABEL } from '../../translations'; import type { LensAttributes } from '../../types'; export const kpiUserAuthenticationsAreaLensAttributes: LensAttributes = { @@ -124,7 +125,7 @@ export const kpiUserAuthenticationsAreaLensAttributes: LensAttributes = { query: 'event.outcome: "failure" ', }, isBucketed: false, - label: 'Fail', + label: FAIL_CHART_LABEL, operationType: 'count', scale: 'ratio', sourceField: '___records___', @@ -157,7 +158,7 @@ export const kpiUserAuthenticationsAreaLensAttributes: LensAttributes = { query: 'event.outcome : "success" ', }, isBucketed: false, - label: 'Succ.', + label: SUCCESS_CHART_LABEL, operationType: 'count', scale: 'ratio', sourceField: '___records___', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_bar.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_bar.test.ts new file mode 100644 index 000000000000..41590d330cd4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_bar.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUserAuthenticationsBarLensAttributes } from './kpi_user_authentications_bar'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiUserAuthenticationsBarLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUserAuthenticationsBarLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.test.ts new file mode 100644 index 000000000000..570e03325ec3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { wrapper } from '../../mocks'; + +import { useLensAttributes } from '../../use_lens_attributes'; + +import { kpiUserAuthenticationsMetricSuccessLensAttributes } from './kpi_user_authentications_metric_success'; + +jest.mock('../../../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ + selectedPatterns: ['auditbeat-mytest-*'], + dataViewId: 'security-solution-my-test', + }), +})); + +jest.mock('../../../../utils/route/use_route_spy', () => ({ + useRouteSpy: jest.fn().mockReturnValue([ + { + detailName: 'mockHost', + pageName: 'users', + tabName: 'events', + }, + ]), +})); + +describe('kpiUserAuthenticationsMetricSuccessLensAttributes', () => { + it('should render', () => { + const { result } = renderHook( + () => + useLensAttributes({ + lensAttributes: kpiUserAuthenticationsMetricSuccessLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.ts index 66f30bf2378a..3af6f5734d45 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success.ts @@ -10,7 +10,7 @@ import type { LensAttributes } from '../../types'; export const kpiUserAuthenticationsMetricSuccessLensAttributes: LensAttributes = { title: '[Host] User authentications - metric success ', description: '', - visualizationType: 'lnsMetric', + visualizationType: 'lnsLegacyMetric', state: { visualization: { accessor: '0eb97c09-a351-4280-97da-944e4bd30dd7', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/mocks.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/mocks.tsx new file mode 100644 index 000000000000..7f4e7e4e6d6e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/mocks.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { cloneDeep } from 'lodash/fp'; + +import { + TestProviders, + mockGlobalState, + SUB_PLUGINS_REDUCER, + kibanaObservable, + createSecuritySolutionStorageMock, +} from '../../mock'; +import type { State } from '../../store'; +import { createStore } from '../../store'; + +export const queryFromSearchBar = { + query: 'host.name: *', + language: 'kql', +}; + +export const filterFromSearchBar = [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'host.id', + params: { + query: '123', + }, + }, + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }, +]; + +export const mockCreateStoreWithQueryFilters = () => { + const { storage } = createSecuritySolutionStorageMock(); + + const state: State = mockGlobalState; + + const myState = cloneDeep(state); + + myState.inputs = { + ...myState.inputs, + global: { + ...myState.inputs.global, + query: queryFromSearchBar, + filters: filterFromSearchBar, + }, + }; + return createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); +}; + +export const wrapper = ({ children }: { children: React.ReactElement }) => ( + {children} +); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/translations.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/translations.ts index 9814a98817ef..4dcceb29323b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/translations.ts @@ -67,9 +67,41 @@ export const SUCCESS_CHART_LABEL = i18n.translate( } ); +export const AUTHENCICATION_SUCCESS_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.visualizationActions.userAuthentications.authentication.successChartLabel', + { + defaultMessage: 'Success', + } +); + export const FAIL_CHART_LABEL = i18n.translate( 'xpack.securitySolution.visualizationActions.userAuthentications.failChartLabel', { defaultMessage: 'Fail', } ); + +export const AUTHENCICATION_FAILURE_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.visualizationActions.userAuthentications.authentication.failureChartLabel', + { + defaultMessage: 'Failure', + } +); + +export const UNIQUE_COUNT = (field: string) => + i18n.translate('xpack.securitySolution.visualizationActions.uniqueCountLabel', { + values: { field }, + + defaultMessage: 'Unique count of {field}', + }); + +export const TOP_VALUE = (field: string) => + i18n.translate('xpack.securitySolution.visualizationActions.topValueLabel', { + values: { field }, + + defaultMessage: 'Top values of {field}', + }); + +export const COUNT = i18n.translate('xpack.securitySolution.visualizationActions.countLabel', { + defaultMessage: 'Count of records', +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx index 7db97ab5526a..76e2f54c62ce 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx @@ -6,21 +6,12 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import React from 'react'; -import { cloneDeep } from 'lodash/fp'; -import { - TestProviders, - mockGlobalState, - SUB_PLUGINS_REDUCER, - kibanaObservable, - createSecuritySolutionStorageMock, -} from '../../mock'; import { getExternalAlertLensAttributes } from './lens_attributes/common/external_alert'; import { useLensAttributes } from './use_lens_attributes'; import { hostNameExistsFilter, getHostDetailsPageFilter, getIndexFilters } from './utils'; -import type { State } from '../../store'; -import { createStore } from '../../store'; + +import { filterFromSearchBar, queryFromSearchBar, wrapper } from './mocks'; jest.mock('../../containers/sourcerer', () => ({ useSourcererDataView: jest.fn().mockReturnValue({ @@ -40,51 +31,7 @@ jest.mock('../../utils/route/use_route_spy', () => ({ })); describe('useLensAttributes', () => { - const state: State = mockGlobalState; - const { storage } = createSecuritySolutionStorageMock(); - const queryFromSearchBar = { - query: 'host.name: *', - language: 'kql', - }; - - const filterFromSearchBar = [ - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: 'host.id', - params: { - query: '123', - }, - }, - query: { - match_phrase: { - 'host.id': '123', - }, - }, - }, - ]; - let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - beforeEach(() => { - const myState = cloneDeep(state); - myState.inputs = { - ...myState.inputs, - global: { - ...myState.inputs.global, - query: queryFromSearchBar, - filters: filterFromSearchBar, - }, - }; - store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - }); - it('should add query', () => { - const wrapper = ({ children }: { children: React.ReactElement }) => ( - {children} - ); const { result } = renderHook( () => useLensAttributes({ @@ -94,13 +41,10 @@ describe('useLensAttributes', () => { { wrapper } ); - expect(result?.current?.state.query).toEqual({ query: 'host.name: *', language: 'kql' }); + expect(result?.current?.state.query).toEqual(queryFromSearchBar); }); it('should add filters', () => { - const wrapper = ({ children }: { children: React.ReactElement }) => ( - {children} - ); const { result } = renderHook( () => useLensAttributes({ @@ -120,9 +64,6 @@ describe('useLensAttributes', () => { }); it('should add data view id to references', () => { - const wrapper = ({ children }: { children: React.ReactElement }) => ( - {children} - ); const { result } = renderHook( () => useLensAttributes({ From 4700107d791ad8402d0fd9d04e9fd33019d80655 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 29 Sep 2022 11:47:18 +0200 Subject: [PATCH 105/185] Add panel loader to entity analytics risk panels (#142122) --- .../entity_analytics/host_risk_score/index.tsx | 11 ++++++++--- .../entity_analytics/user_risk_score/index.tsx | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx index 794b33c6b33e..49df0b1b0e45 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useEffect, useMemo, useState } from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import { RiskScoresDeprecated } from '../../../../common/components/risk_score/risk_score_deprecated'; @@ -35,6 +35,8 @@ import { EntityAnalyticsHostRiskScoreDisable } from '../../../../common/componen import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries'; +import { Loader } from '../../../../common/components/loader'; +import { Panel } from '../../../../common/components/panel'; const TABLE_QUERY_ID = 'hostRiskDashboardTable'; const HOST_RISK_KPI_QUERY_ID = 'headerHostRiskScoreKpiQuery'; @@ -149,7 +151,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { return ( - + } titleSize="s" @@ -207,7 +209,10 @@ const EntityAnalyticsHostRiskScoresComponent = () => { )} - + {(isTableLoading || isKpiLoading) && ( + + )} + ); }; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx index 3b3905d4604a..034e62bb37ad 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useEffect, useMemo, useState } from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import { RiskScoresDeprecated } from '../../../../common/components/risk_score/risk_score_deprecated'; import { SeverityFilterGroup } from '../../../../common/components/severity/severity_filter_group'; @@ -35,6 +35,8 @@ import { EntityAnalyticsUserRiskScoreDisable } from '../../../../common/componen import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries'; +import { Loader } from '../../../../common/components/loader'; +import { Panel } from '../../../../common/components/panel'; const TABLE_QUERY_ID = 'userRiskDashboardTable'; const USER_RISK_KPI_QUERY_ID = 'headerUserRiskScoreKpiQuery'; @@ -149,7 +151,7 @@ const EntityAnalyticsUserRiskScoresComponent = () => { return ( - + } titleSize="s" @@ -207,7 +209,10 @@ const EntityAnalyticsUserRiskScoresComponent = () => { )} - + {(isTableLoading || isKpiLoading) && ( + + )} + ); }; From 3f9687e3f06527a873933f29de5e261555041406 Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Thu, 29 Sep 2022 12:22:58 +0200 Subject: [PATCH 106/185] [Actionable observability] Alert summary widget new design (#141236) * Implement new alert summary widget design and remove chart * Fix padding issue * Remove unused translation * Fix type and test errors * Fix types and remove extra aggs logic * Add triggers_actions_ui to storybook list * Fix small alignment issue by removing extra EuiFlexGroup * Fix UI issue for small mobile * Update spacing Co-authored-by: Kevin Delemme Co-authored-by: Kevin Delemme Co-authored-by: Xavier Mouligneau --- .../steps/storybooks/build_and_upload.ts | 1 + src/dev/storybook/aliases.ts | 1 + .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../triggers_actions_ui/.storybook/main.js | 8 + .../triggers_actions_ui/.storybook/preview.js | 10 + .../use_load_rule_alerts_aggregations.test.ts | 11 +- .../use_load_rule_alerts_aggregations.ts | 105 +---- .../mock/rule_details/alert_summary/index.ts | 372 ------------------ .../components/alert_summary_widget_error.tsx | 39 ++ .../alert_summary_widget_ui.stories.tsx | 21 + .../components/alert_summary_widget_ui.tsx | 87 ++++ .../alert_summary/components/index.ts | 9 + .../alert_summary/components/types.ts | 12 + .../components/alert_summary/helpers.tsx | 41 -- .../components/alert_summary/index.tsx | 3 +- .../rule_alerts_summary.test.tsx | 173 -------- .../alert_summary/rule_alerts_summary.tsx | 200 +--------- .../components/alert_summary/types.ts | 9 - 20 files changed, 209 insertions(+), 896 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/.storybook/main.js create mode 100644 x-pack/plugins/triggers_actions_ui/.storybook/preview.js create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_error.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.stories.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/types.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/helpers.tsx diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.ts b/.buildkite/scripts/steps/storybooks/build_and_upload.ts index 8575ee683d82..dcceca784891 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.ts +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.ts @@ -41,6 +41,7 @@ const STORYBOOKS = [ 'presentation', 'security_solution', 'shared_ux', + 'triggers_actions_ui', 'ui_actions_enhanced', 'unified_search', ]; diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 1705c9ac2ec9..b4224e154def 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -43,6 +43,7 @@ export const storybookAliases = { security_solution: 'x-pack/plugins/security_solution/.storybook', shared_ux: 'packages/shared-ux/storybook/config', threat_intelligence: 'x-pack/plugins/threat_intelligence/.storybook', + triggers_actions_ui: 'x-pack/plugins/triggers_actions_ui/.storybook', ui_actions_enhanced: 'src/plugins/ui_actions_enhanced/.storybook', unified_search: 'src/plugins/unified_search/.storybook', }; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index cdaa18f14c6e..60b692e720ec 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -32529,7 +32529,6 @@ "xpack.triggersActionsUI.sections.ruleDetails.alertsList.ruleTypeExcessDurationMessage": "La durée dépasse le temps d'exécution attendu de la règle.", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.activeLabel": "Actif", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.allAlertsLabel": "Toutes les alertes", - "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.recentAlertHistoryTitle": "Historique récent des alertes", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.title": "Alertes", "xpack.triggersActionsUI.sections.ruleDetails.deleteRuleButtonLabel": "Supprimer la règle", "xpack.triggersActionsUI.sections.ruleDetails.disableRuleButtonLabel": "Désactiver", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a7d49509c775..0aa5fa39b93e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -32503,7 +32503,6 @@ "xpack.triggersActionsUI.sections.ruleDetails.alertsList.ruleTypeExcessDurationMessage": "期間がルールの想定実行時間を超えています。", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.activeLabel": "アクティブ", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.allAlertsLabel": "すべてのアラート", - "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.recentAlertHistoryTitle": "最近のアラート履歴", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.title": "アラート", "xpack.triggersActionsUI.sections.ruleDetails.deleteRuleButtonLabel": "ルールの削除", "xpack.triggersActionsUI.sections.ruleDetails.disableRuleButtonLabel": "無効にする", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 25bd585b1343..baf98e70b8dd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -32540,7 +32540,6 @@ "xpack.triggersActionsUI.sections.ruleDetails.alertsList.ruleTypeExcessDurationMessage": "持续时间超出了规则的预期运行时间。", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.activeLabel": "活动", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.allAlertsLabel": "所有告警", - "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.recentAlertHistoryTitle": "最近告警历史记录", "xpack.triggersActionsUI.sections.ruleDetails.alertsSummary.title": "告警", "xpack.triggersActionsUI.sections.ruleDetails.deleteRuleButtonLabel": "删除规则", "xpack.triggersActionsUI.sections.ruleDetails.disableRuleButtonLabel": "禁用", diff --git a/x-pack/plugins/triggers_actions_ui/.storybook/main.js b/x-pack/plugins/triggers_actions_ui/.storybook/main.js new file mode 100644 index 000000000000..86b48c32f103 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/.storybook/main.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/x-pack/plugins/triggers_actions_ui/.storybook/preview.js b/x-pack/plugins/triggers_actions_ui/.storybook/preview.js new file mode 100644 index 000000000000..3200746243d4 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/.storybook/preview.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiThemeProviderDecorator } from '@kbn/kibana-react-plugin/common'; + +export const decorators = [EuiThemeProviderDecorator]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.test.ts index 293c1e992ac1..03be9f6b9bd5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.test.ts @@ -8,7 +8,7 @@ import { ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; import { renderHook } from '@testing-library/react-hooks'; import { useKibana } from '../../common/lib/kibana'; -import { mockAggsResponse, mockChartData } from '../mock/rule_details/alert_summary'; +import { mockAggsResponse } from '../mock/rule_details/alert_summary'; import { useLoadRuleAlertsAggs } from './use_load_rule_alerts_aggregations'; jest.mock('../../common/lib/kibana'); @@ -21,7 +21,7 @@ describe('useLoadRuleAlertsAggs', () => { useKibanaMock().services.http.get = jest.fn().mockResolvedValue({ index_name: ['mock_index'] }); }); - it('should return the expected chart data from the Elasticsearch Aggs. query', async () => { + it('should return the expected data from the Elasticsearch Aggs. query', async () => { const { result, waitForNextUpdate } = renderHook(() => useLoadRuleAlertsAggs({ features: ALERTS_FEATURE_ID, @@ -31,18 +31,15 @@ describe('useLoadRuleAlertsAggs', () => { expect(result.current).toEqual({ isLoadingRuleAlertsAggs: true, ruleAlertsAggs: { active: 0, recovered: 0 }, - alertsChartData: [], }); await waitForNextUpdate(); - const { ruleAlertsAggs, errorRuleAlertsAggs, alertsChartData } = result.current; + const { ruleAlertsAggs, errorRuleAlertsAggs } = result.current; expect(ruleAlertsAggs).toEqual({ active: 1, recovered: 7, }); - expect(alertsChartData).toEqual(mockChartData()); expect(errorRuleAlertsAggs).toBeFalsy(); - expect(alertsChartData.length).toEqual(33); }); it('should have the correct query body sent to Elasticsearch', async () => { @@ -55,7 +52,7 @@ describe('useLoadRuleAlertsAggs', () => { ); await waitForNextUpdate(); - const body = `{"index":"mock_index","size":0,"query":{"bool":{"must":[{"term":{"kibana.alert.rule.uuid":"${ruleId}"}},{"range":{"@timestamp":{"gte":"now-30d","lt":"now"}}},{"bool":{"should":[{"term":{"kibana.alert.status":"active"}},{"term":{"kibana.alert.status":"recovered"}}]}}]}},"aggs":{"total":{"filters":{"filters":{"totalActiveAlerts":{"term":{"kibana.alert.status":"active"}},"totalRecoveredAlerts":{"term":{"kibana.alert.status":"recovered"}}}}},"statusPerDay":{"date_histogram":{"field":"@timestamp","fixed_interval":"1d","extended_bounds":{"min":"now-30d","max":"now"}},"aggs":{"alertStatus":{"terms":{"field":"kibana.alert.status"}}}}}}`; + const body = `{"index":"mock_index","size":0,"query":{"bool":{"must":[{"term":{"kibana.alert.rule.uuid":"${ruleId}"}},{"range":{"@timestamp":{"gte":"now-30d","lt":"now"}}},{"bool":{"should":[{"term":{"kibana.alert.status":"active"}},{"term":{"kibana.alert.status":"recovered"}}]}}]}},"aggs":{"total":{"filters":{"filters":{"totalActiveAlerts":{"term":{"kibana.alert.status":"active"}},"totalRecoveredAlerts":{"term":{"kibana.alert.status":"recovered"}}}}}}}`; expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( '/internal/rac/alerts/find', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.ts index c938b0b2cc13..3df1722abc00 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_alerts_aggregations.ts @@ -10,7 +10,6 @@ import { AsApiContract } from '@kbn/actions-plugin/common'; import { HttpSetup } from '@kbn/core/public'; import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/constants'; import { useKibana } from '../../common/lib/kibana'; -import { AlertChartData } from '../sections/rule_details/components/alert_summary'; interface UseLoadRuleAlertsAggs { features: string; @@ -29,7 +28,6 @@ interface LoadRuleAlertsAggs { recovered: number; }; errorRuleAlertsAggs?: string; - alertsChartData: AlertChartData[]; } interface IndexName { index: string; @@ -40,7 +38,6 @@ export function useLoadRuleAlertsAggs({ features, ruleId }: UseLoadRuleAlertsAgg const [ruleAlertsAggs, setRuleAlertsAggs] = useState({ isLoadingRuleAlertsAggs: true, ruleAlertsAggs: { active: 0, recovered: 0 }, - alertsChartData: [], }); const isCancelledRef = useRef(false); const abortCtrlRef = useRef(new AbortController()); @@ -54,7 +51,7 @@ export function useLoadRuleAlertsAggs({ features, ruleId }: UseLoadRuleAlertsAgg http, features, }); - const { active, recovered, error, alertsChartData } = await fetchRuleAlertsAggByTimeRange({ + const { active, recovered, error } = await fetchRuleAlertsAggByTimeRange({ http, index, ruleId, @@ -68,7 +65,6 @@ export function useLoadRuleAlertsAggs({ features, ruleId }: UseLoadRuleAlertsAgg active, recovered, }, - alertsChartData, isLoadingRuleAlertsAggs: false, })); } @@ -79,7 +75,6 @@ export function useLoadRuleAlertsAggs({ features, ruleId }: UseLoadRuleAlertsAgg ...oldState, isLoadingRuleAlertsAggs: false, errorRuleAlertsAggs: error, - alertsChartData: [], })); } } @@ -92,7 +87,7 @@ export function useLoadRuleAlertsAggs({ features, ruleId }: UseLoadRuleAlertsAgg return ruleAlertsAggs; } -export async function fetchIndexNameAPI({ +async function fetchIndexNameAPI({ http, features, }: { @@ -107,14 +102,7 @@ export async function fetchIndexNameAPI({ }; } -interface RuleAlertsAggs { - active: number; - recovered: number; - error?: string; - alertsChartData: AlertChartData[]; -} - -export async function fetchRuleAlertsAggByTimeRange({ +async function fetchRuleAlertsAggByTimeRange({ http, index, ruleId, @@ -183,109 +171,22 @@ export async function fetchRuleAlertsAggByTimeRange({ }, }, }, - statusPerDay: { - date_histogram: { - field: '@timestamp', - fixed_interval: '1d', - extended_bounds: { - min: 'now-30d', - max: 'now', - }, - }, - aggs: { - alertStatus: { - terms: { - field: 'kibana.alert.status', - }, - }, - }, - }, }, }), }); const active = res?.aggregations?.total.buckets.totalActiveAlerts?.doc_count ?? 0; const recovered = res?.aggregations?.total.buckets.totalRecoveredAlerts?.doc_count ?? 0; - let maxTotalAlertPerDay = 0; - res?.aggregations?.statusPerDay.buckets.forEach( - (dayAlerts: { - key: number; - doc_count: number; - alertStatus: { - buckets: Array<{ - key: 'active' | 'recovered'; - doc_count: number; - }>; - }; - }) => { - if (dayAlerts.doc_count > maxTotalAlertPerDay) { - maxTotalAlertPerDay = dayAlerts.doc_count; - } - } - ); - const alertsChartData = [ - ...res?.aggregations?.statusPerDay.buckets.reduce( - ( - acc: AlertChartData[], - dayAlerts: { - key: number; - doc_count: number; - alertStatus: { - buckets: Array<{ - key: 'active' | 'recovered'; - doc_count: number; - }>; - }; - } - ) => { - // We are adding this to each day to construct the 30 days bars (background bar) when there is no data for a given day or to show the delta today alerts/total alerts. - const totalDayAlerts = { - date: dayAlerts.key, - count: maxTotalAlertPerDay === 0 ? 1 : maxTotalAlertPerDay, - status: 'total', - }; - - if (dayAlerts.doc_count > 0) { - const localAlertChartData = acc; - // If there are alerts in this day, we construct the chart data - dayAlerts.alertStatus.buckets.forEach((alert) => { - localAlertChartData.push({ - date: dayAlerts.key, - count: alert.doc_count, - status: alert.key, - }); - }); - const deltaAlertsCount = maxTotalAlertPerDay - dayAlerts.doc_count; - if (deltaAlertsCount > 0) { - localAlertChartData.push({ - date: dayAlerts.key, - count: deltaAlertsCount, - status: 'total', - }); - } - return localAlertChartData; - } - return [...acc, totalDayAlerts]; - }, - [] - ), - ]; return { active, recovered, - alertsChartData: [ - ...alertsChartData.filter((acd) => acd.status === 'recovered'), - ...alertsChartData.filter((acd) => acd.status === 'active'), - ...alertsChartData.filter((acd) => acd.status === 'total'), - ], }; } catch (error) { return { error, active: 0, recovered: 0, - alertsChartData: [], } as RuleAlertsAggs; } } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/mock/rule_details/alert_summary/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/mock/rule_details/alert_summary/index.ts index b0e5416bbf63..2d5af068ae55 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/mock/rule_details/alert_summary/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/mock/rule_details/alert_summary/index.ts @@ -6,7 +6,6 @@ */ import { Rule } from '../../../../types'; -import { AlertChartData } from '../../../sections/rule_details/components/alert_summary'; export const mockRule = (): Rule => { return { @@ -63,383 +62,12 @@ export const mockRule = (): Rule => { }; }; -export function mockChartData(): AlertChartData[] { - return [ - { - date: 1660608000000, - count: 6, - status: 'recovered', - }, - { - date: 1660694400000, - count: 1, - status: 'recovered', - }, - { - date: 1660694400000, - count: 1, - status: 'active', - }, - { - date: 1658102400000, - count: 6, - status: 'total', - }, - { - date: 1658188800000, - count: 6, - status: 'total', - }, - { - date: 1658275200000, - count: 6, - status: 'total', - }, - { - date: 1658361600000, - count: 6, - status: 'total', - }, - { - date: 1658448000000, - count: 6, - status: 'total', - }, - { - date: 1658534400000, - count: 6, - status: 'total', - }, - { - date: 1658620800000, - count: 6, - status: 'total', - }, - { - date: 1658707200000, - count: 6, - status: 'total', - }, - { - date: 1658793600000, - count: 6, - status: 'total', - }, - { - date: 1658880000000, - count: 6, - status: 'total', - }, - { - date: 1658966400000, - count: 6, - status: 'total', - }, - { - date: 1659052800000, - count: 6, - status: 'total', - }, - { - date: 1659139200000, - count: 6, - status: 'total', - }, - { - date: 1659225600000, - count: 6, - status: 'total', - }, - { - date: 1659312000000, - count: 6, - status: 'total', - }, - { - date: 1659398400000, - count: 6, - status: 'total', - }, - { - date: 1659484800000, - count: 6, - status: 'total', - }, - { - date: 1659571200000, - count: 6, - status: 'total', - }, - { - date: 1659657600000, - count: 6, - status: 'total', - }, - { - date: 1659744000000, - count: 6, - status: 'total', - }, - { - date: 1659830400000, - count: 6, - status: 'total', - }, - { - date: 1659916800000, - count: 6, - status: 'total', - }, - { - date: 1660003200000, - count: 6, - status: 'total', - }, - { - date: 1660089600000, - count: 6, - status: 'total', - }, - { - date: 1660176000000, - count: 6, - status: 'total', - }, - { - date: 1660262400000, - count: 6, - status: 'total', - }, - { - date: 1660348800000, - count: 6, - status: 'total', - }, - { - date: 1660435200000, - count: 6, - status: 'total', - }, - { - date: 1660521600000, - count: 6, - status: 'total', - }, - { - date: 1660694400000, - count: 4, - status: 'total', - }, - ]; -} - export const mockAggsResponse = () => { return { aggregations: { total: { buckets: { totalActiveAlerts: { doc_count: 1 }, totalRecoveredAlerts: { doc_count: 7 } }, }, - statusPerDay: { - buckets: [ - { - key_as_string: '2022-07-18T00:00:00.000Z', - key: 1658102400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-19T00:00:00.000Z', - key: 1658188800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-20T00:00:00.000Z', - key: 1658275200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-21T00:00:00.000Z', - key: 1658361600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-22T00:00:00.000Z', - key: 1658448000000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-23T00:00:00.000Z', - key: 1658534400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-24T00:00:00.000Z', - key: 1658620800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-25T00:00:00.000Z', - key: 1658707200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-26T00:00:00.000Z', - key: 1658793600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-27T00:00:00.000Z', - key: 1658880000000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-28T00:00:00.000Z', - key: 1658966400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-29T00:00:00.000Z', - key: 1659052800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-30T00:00:00.000Z', - key: 1659139200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-07-31T00:00:00.000Z', - key: 1659225600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-01T00:00:00.000Z', - key: 1659312000000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-02T00:00:00.000Z', - key: 1659398400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-03T00:00:00.000Z', - key: 1659484800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-04T00:00:00.000Z', - key: 1659571200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-05T00:00:00.000Z', - key: 1659657600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-06T00:00:00.000Z', - key: 1659744000000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-07T00:00:00.000Z', - key: 1659830400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-08T00:00:00.000Z', - key: 1659916800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-09T00:00:00.000Z', - key: 1660003200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-10T00:00:00.000Z', - key: 1660089600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-11T00:00:00.000Z', - key: 1660176000000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-12T00:00:00.000Z', - key: 1660262400000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-13T00:00:00.000Z', - key: 1660348800000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-14T00:00:00.000Z', - key: 1660435200000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-15T00:00:00.000Z', - key: 1660521600000, - doc_count: 0, - alertStatus: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, - }, - { - key_as_string: '2022-08-16T00:00:00.000Z', - key: 1660608000000, - doc_count: 6, - alertStatus: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [{ key: 'recovered', doc_count: 6 }], - }, - }, - { - key_as_string: '2022-08-17T00:00:00.000Z', - key: 1660694400000, - doc_count: 2, - alertStatus: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'active', doc_count: 1 }, - { key: 'recovered', doc_count: 1 }, - ], - }, - }, - ], - }, }, }; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_error.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_error.tsx new file mode 100644 index 000000000000..1ee04fe2f69d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_error.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const AlertSummaryWidgetError = () => { + return ( + + + + } + body={ +

    + { + + } +

    + } + /> + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.stories.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.stories.tsx new file mode 100644 index 000000000000..ab064893ae6f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.stories.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AlertsSummaryWidgetUI as Component } from './alert_summary_widget_ui'; + +export default { + component: Component, + title: 'app/AlertsSummaryWidgetUI', +}; + +export const Overview = { + args: { + active: 15, + recovered: 53, + timeRange: 'Last 30 days', + }, +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.tsx new file mode 100644 index 000000000000..e4003e803032 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/alert_summary_widget_ui.tsx @@ -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 { euiLightVars } from '@kbn/ui-theme'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { AlertsSummaryWidgetUIProps } from './types'; + +export const AlertsSummaryWidgetUI = ({ + active, + recovered, + timeRange, +}: AlertsSummaryWidgetUIProps) => { + return ( + + + + + + +
    + +
    +
    + {!!timeRange && ( + <> + + + {timeRange} + + + )} +
    + + + + + +

    {active + recovered}

    +
    + + + +
    + + + +

    {recovered}

    +
    +
    + + + +
    + + +

    {active}

    +
    + + + +
    +
    +
    +
    +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/index.ts new file mode 100644 index 000000000000..e1f4ef4d231e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { AlertsSummaryWidgetUI } from './alert_summary_widget_ui'; +export { AlertSummaryWidgetError } from './alert_summary_widget_error'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/types.ts new file mode 100644 index 000000000000..81437071fcea --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/components/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 interface AlertsSummaryWidgetUIProps { + active: number; + recovered: number; + timeRange: JSX.Element | string; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/helpers.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/helpers.tsx deleted file mode 100644 index 167c98a678f5..000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/helpers.tsx +++ /dev/null @@ -1,41 +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 { LIGHT_THEME, XYChartSeriesIdentifier } from '@elastic/charts'; -import { AlertChartData } from './types'; - -export const formatChartAlertData = ( - data: AlertChartData[] -): Array<{ x: number; y: number; g: string }> => - data.map((alert) => ({ - x: alert.date, - y: alert.count, - g: alert.status, - })); - -export const getColorSeries = ({ seriesKeys }: XYChartSeriesIdentifier) => { - switch (seriesKeys[0]) { - case 'active': - return LIGHT_THEME.colors.vizColors[2]; - case 'recovered': - return LIGHT_THEME.colors.vizColors[1]; - case 'total': - return '#f5f7fa'; - default: - return null; - } -}; - -/** - * This function may be passed to `Array.find()` to locate the `P1DT` - * configuration (sub) setting, a string array that contains two entries - * like the following example: `['P1DT', 'YYYY-MM-DD']`. - */ -export const isP1DTFormatterSetting = (formatNameFormatterPair?: string[]) => - Array.isArray(formatNameFormatterPair) && - formatNameFormatterPair[0] === 'P1DT' && - formatNameFormatterPair.length === 2; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/index.tsx index 80f221f436c4..ede15d4bac29 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/index.tsx @@ -6,5 +6,4 @@ */ export { RuleAlertsSummary } from './rule_alerts_summary'; -export type { RuleAlertsSummaryProps, AlertChartData, AlertsChartProps } from './types'; -export { formatChartAlertData, getColorSeries } from './helpers'; +export type { RuleAlertsSummaryProps } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.test.tsx index 39264020f30a..019d4f2ba549 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.test.tsx @@ -12,7 +12,6 @@ import { mount, ReactWrapper } from 'enzyme'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; import { mockRule } from '../../../../mock/rule_details/alert_summary'; -import { AlertChartData } from './types'; jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ useUiSetting: jest.fn().mockImplementation(() => true), @@ -25,7 +24,6 @@ jest.mock('../../../../hooks/use_load_rule_types', () => ({ jest.mock('../../../../hooks/use_load_rule_alerts_aggregations', () => ({ useLoadRuleAlertsAggs: jest.fn().mockReturnValue({ ruleAlertsAggs: { active: 1, recovered: 7 }, - alertsChartData: mockChartData(), }), })); @@ -80,174 +78,3 @@ describe('Rule Alert Summary', () => { expect(wrapper.find('[data-test-subj="totalAlertsCount"]').text()).toBe('8'); }); }); - -// This function should stay in the same file as the test otherwise the test will fail. -function mockChartData(): AlertChartData[] { - return [ - { - date: 1660608000000, - count: 6, - status: 'recovered', - }, - { - date: 1660694400000, - count: 1, - status: 'recovered', - }, - { - date: 1660694400000, - count: 1, - status: 'active', - }, - { - date: 1658102400000, - count: 6, - status: 'total', - }, - { - date: 1658188800000, - count: 6, - status: 'total', - }, - { - date: 1658275200000, - count: 6, - status: 'total', - }, - { - date: 1658361600000, - count: 6, - status: 'total', - }, - { - date: 1658448000000, - count: 6, - status: 'total', - }, - { - date: 1658534400000, - count: 6, - status: 'total', - }, - { - date: 1658620800000, - count: 6, - status: 'total', - }, - { - date: 1658707200000, - count: 6, - status: 'total', - }, - { - date: 1658793600000, - count: 6, - status: 'total', - }, - { - date: 1658880000000, - count: 6, - status: 'total', - }, - { - date: 1658966400000, - count: 6, - status: 'total', - }, - { - date: 1659052800000, - count: 6, - status: 'total', - }, - { - date: 1659139200000, - count: 6, - status: 'total', - }, - { - date: 1659225600000, - count: 6, - status: 'total', - }, - { - date: 1659312000000, - count: 6, - status: 'total', - }, - { - date: 1659398400000, - count: 6, - status: 'total', - }, - { - date: 1659484800000, - count: 6, - status: 'total', - }, - { - date: 1659571200000, - count: 6, - status: 'total', - }, - { - date: 1659657600000, - count: 6, - status: 'total', - }, - { - date: 1659744000000, - count: 6, - status: 'total', - }, - { - date: 1659830400000, - count: 6, - status: 'total', - }, - { - date: 1659916800000, - count: 6, - status: 'total', - }, - { - date: 1660003200000, - count: 6, - status: 'total', - }, - { - date: 1660089600000, - count: 6, - status: 'total', - }, - { - date: 1660176000000, - count: 6, - status: 'total', - }, - { - date: 1660262400000, - count: 6, - status: 'total', - }, - { - date: 1660348800000, - count: 6, - status: 'total', - }, - { - date: 1660435200000, - count: 6, - status: 'total', - }, - { - date: 1660521600000, - count: 6, - status: 'total', - }, - { - date: 1660694400000, - count: 4, - status: 'total', - }, - ]; -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.tsx index 708b12ceb7b4..da4378356865 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/rule_alerts_summary.tsx @@ -5,65 +5,17 @@ * 2.0. */ -import { - BarSeries, - Chart, - FilterPredicate, - LIGHT_THEME, - ScaleType, - Settings, - TooltipType, -} from '@elastic/charts'; -import { - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiPanel, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo, useState } from 'react'; -import { - EUI_CHARTS_THEME_DARK, - EUI_CHARTS_THEME_LIGHT, - EUI_SPARKLINE_THEME_PARTIAL, -} from '@elastic/eui/dist/eui_charts_theme'; -import { useUiSetting } from '@kbn/kibana-react-plugin/public'; -import moment from 'moment'; +import React, { useEffect, useState } from 'react'; import { useLoadRuleAlertsAggs } from '../../../../hooks/use_load_rule_alerts_aggregations'; import { useLoadRuleTypes } from '../../../../hooks/use_load_rule_types'; -import { formatChartAlertData, getColorSeries } from '.'; import { RuleAlertsSummaryProps } from '.'; -import { isP1DTFormatterSetting } from './helpers'; +import { AlertSummaryWidgetError, AlertsSummaryWidgetUI } from './components'; -const Y_ACCESSORS = ['y']; -const X_ACCESSORS = ['x']; -const G_ACCESSORS = ['g']; -const FALLBACK_DATE_FORMAT_SCALED_P1DT = 'YYYY-MM-DD'; export const RuleAlertsSummary = ({ rule, filteredRuleTypes }: RuleAlertsSummaryProps) => { const [features, setFeatures] = useState(''); - const isDarkMode = useUiSetting('theme:darkMode'); - - const scaledDateFormatPreference = useUiSetting('dateFormat:scaled'); - const maybeP1DTFormatter = Array.isArray(scaledDateFormatPreference) - ? scaledDateFormatPreference.find(isP1DTFormatterSetting) - : null; - const p1dtFormat = - Array.isArray(maybeP1DTFormatter) && maybeP1DTFormatter.length === 2 - ? maybeP1DTFormatter[1] - : FALLBACK_DATE_FORMAT_SCALED_P1DT; - - const theme = useMemo( - () => [ - EUI_SPARKLINE_THEME_PARTIAL, - isDarkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme, - ], - [isDarkMode] - ); const { ruleTypes } = useLoadRuleTypes({ filteredRuleTypes, }); @@ -71,21 +23,10 @@ export const RuleAlertsSummary = ({ rule, filteredRuleTypes }: RuleAlertsSummary ruleAlertsAggs: { active, recovered }, isLoadingRuleAlertsAggs, errorRuleAlertsAggs, - alertsChartData, } = useLoadRuleAlertsAggs({ ruleId: rule.id, features, }); - const chartData = useMemo(() => formatChartAlertData(alertsChartData), [alertsChartData]); - const tooltipSettings = useMemo( - () => ({ - type: TooltipType.VerticalCursor, - headerFormatter: ({ value }: { value: number }) => { - return <>{moment(value).format(p1dtFormat)}; - }, - }), - [p1dtFormat] - ); useEffect(() => { const matchedRuleType = ruleTypes.find((type) => type.id === rule.ruleTypeId); @@ -95,133 +36,18 @@ export const RuleAlertsSummary = ({ rule, filteredRuleTypes }: RuleAlertsSummary }, [rule, ruleTypes]); if (isLoadingRuleAlertsAggs) return ; - if (errorRuleAlertsAggs) - return ( - - - - } - body={ -

    - { - - } -

    - } - /> - ); - const isVisibleFunction: FilterPredicate = (series) => series.splitAccessors.get('g') !== 'total'; + if (errorRuleAlertsAggs) return ; return ( - - - - - - -
    - -
    -
    - - - - -
    - - - - - - - - - -

    {active + recovered}

    -
    -
    -
    - - - - - - -

    {active}

    -
    -
    -
    - - - - - - - -

    {recovered}

    -
    -
    -
    -
    -
    -
    -
    -
    - - - -
    - -
    -
    -
    -
    - - - - - -
    + } + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/types.ts index ae4d8696572b..7ae05d1eaa10 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/alert_summary/types.ts @@ -11,12 +11,3 @@ export interface RuleAlertsSummaryProps { rule: Rule; filteredRuleTypes: string[]; } -export interface AlertChartData { - status: 'active' | 'recovered' | 'total'; - count: number; - date: number; -} - -export interface AlertsChartProps { - data: AlertChartData[]; -} From 4bab191faf62b88fde6604353a3d32f0b5aaf3b4 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Thu, 29 Sep 2022 11:23:52 +0100 Subject: [PATCH 107/185] [APM] Create infraMetricsClient (#141735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create infraMetricsClient * remove infraclient from setup request and create its own * Remove the setupInfraMetrics, use createInfraMetricsClient * fix types * typing createInfraMetrics client * Fix types and simplify `get_host_names` * fix get_service_overview_container_metadata types * pass resources as a param instead Co-authored-by: Søren Louv-Jansen --- .../create_infra_metrics_client.ts | 41 +++++++ .../routes/infrastructure/get_host_names.ts | 113 +++++------------- .../apm/server/routes/infrastructure/route.ts | 11 +- ...get_service_instance_container_metadata.ts | 17 +-- ...get_service_overview_container_metadata.ts | 38 +----- .../apm/server/routes/services/route.ts | 35 ++---- 6 files changed, 93 insertions(+), 162 deletions(-) create mode 100644 x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts new file mode 100644 index 000000000000..4cd9df6bf213 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types'; +import { APMRouteHandlerResources } from '../../../../routes/typings'; +import { getInfraMetricIndices } from '../../get_infra_metric_indices'; + +type InfraMetricsSearchParams = Omit; + +export type InfraMetricsClient = ReturnType; + +export function createInfraMetricsClient(resources: APMRouteHandlerResources) { + return { + async search( + opts: TParams + ): Promise> { + const { + savedObjects: { client: savedObjectsClient }, + elasticsearch: { client: esClient }, + } = await resources.context.core; + + const indexName = await getInfraMetricIndices({ + infraPlugin: resources.plugins.infra, + savedObjectsClient, + }); + + const searchParams = { + index: [indexName], + ...opts, + }; + + return esClient.asCurrentUser.search( + searchParams + ) as Promise; + }, + }; +} diff --git a/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts b/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts index 4890f8ab909e..8f47c2d1664b 100644 --- a/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts +++ b/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts @@ -5,106 +5,55 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core/server'; import { rangeQuery } from '@kbn/observability-plugin/server'; -import { InfraPluginStart, InfraPluginSetup } from '@kbn/infra-plugin/server'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { CONTAINER_ID, HOST_NAME, } from '../../../common/elasticsearch_fieldnames'; -import { ApmPluginRequestHandlerContext } from '../typings'; -import { getInfraMetricIndices } from '../../lib/helpers/get_infra_metric_indices'; +import { InfraMetricsClient } from '../../lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client'; -interface Aggs extends estypes.AggregationsMultiBucketAggregateBase { - buckets: Array<{ - key: string; - key_as_string?: string; - }>; -} - -interface InfraPlugin { - setup: InfraPluginSetup; - start: () => Promise; -} - -const getHostNames = async ({ - esClient, +export async function getContainerHostNames({ containerIds, - index, + infraMetricsClient, start, end, }: { - esClient: ElasticsearchClient; containerIds: string[]; - index: string; + infraMetricsClient: InfraMetricsClient; start: number; end: number; -}) => { - const response = await esClient.search({ - index: [index], - body: { - size: 0, - query: { - bool: { - filter: [ - { - terms: { - [CONTAINER_ID]: containerIds, - }, +}): Promise { + if (!containerIds.length) { + return []; + } + + const response = await infraMetricsClient.search({ + size: 0, + query: { + bool: { + filter: [ + { + terms: { + [CONTAINER_ID]: containerIds, }, - ...rangeQuery(start, end), - ], - }, - }, - aggs: { - hostNames: { - terms: { - field: HOST_NAME, - size: 500, }, + ...rangeQuery(start, end), + ], + }, + }, + aggs: { + hostNames: { + terms: { + field: HOST_NAME, + size: 500, }, }, }, }); - return { - hostNames: - response.aggregations?.hostNames?.buckets.map( - (bucket) => bucket.key as string - ) ?? [], - }; -}; + const hostNames = response.aggregations?.hostNames?.buckets.map( + (bucket) => bucket.key as string + ); -export const getContainerHostNames = async ({ - containerIds, - context, - infra, - start, - end, -}: { - containerIds: string[]; - context: ApmPluginRequestHandlerContext; - infra: InfraPlugin; - start: number; - end: number; -}): Promise => { - if (containerIds.length) { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; - const savedObjectsClient = (await context.core).savedObjects.client; - const metricIndices = await getInfraMetricIndices({ - infraPlugin: infra, - savedObjectsClient, - }); - - const containerHostNames = await getHostNames({ - esClient, - containerIds, - index: metricIndices, - start, - end, - }); - return containerHostNames.hostNames; - } - return []; -}; + return hostNames ?? []; +} diff --git a/x-pack/plugins/apm/server/routes/infrastructure/route.ts b/x-pack/plugins/apm/server/routes/infrastructure/route.ts index 756b29d5d457..678f380bc4fd 100644 --- a/x-pack/plugins/apm/server/routes/infrastructure/route.ts +++ b/x-pack/plugins/apm/server/routes/infrastructure/route.ts @@ -10,6 +10,7 @@ import { setupRequest } from '../../lib/helpers/setup_request'; import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; import { getInfrastructureData } from './get_infrastructure_data'; import { getContainerHostNames } from './get_host_names'; +import { createInfraMetricsClient } from '../../lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client'; const infrastructureRoute = createApmServerRoute({ endpoint: @@ -29,12 +30,9 @@ const infrastructureRoute = createApmServerRoute({ podNames: string[]; }> => { const setup = await setupRequest(resources); + const infraMetricsClient = createInfraMetricsClient(resources); - const { - context, - params, - plugins: { infra }, - } = resources; + const { params } = resources; const { path: { serviceName }, @@ -54,8 +52,7 @@ const infrastructureRoute = createApmServerRoute({ // due some limitations on the data we get from apm-metrics indices, if we have a service running in a container we want to query, to get the host.name, filtering by container.id const containerHostNames = await getContainerHostNames({ containerIds, - context, - infra, + infraMetricsClient, start, end, }); diff --git a/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts b/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts index 729c2f1f1b1e..ebce4f933871 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_instance_container_metadata.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core/server'; import { rangeQuery } from '@kbn/observability-plugin/server'; import { - CONTAINER, CONTAINER_ID, CONTAINER_IMAGE, KUBERNETES, @@ -21,6 +19,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { Kubernetes } from '../../../typings/es_schemas/raw/fields/kubernetes'; import { maybe } from '../../../common/utils/maybe'; +import { InfraMetricsClient } from '../../lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client'; type ServiceInstanceContainerMetadataDetails = | { @@ -29,22 +28,16 @@ type ServiceInstanceContainerMetadataDetails = | undefined; export const getServiceInstanceContainerMetadata = async ({ - esClient, - indexName, + infraMetricsClient, containerId, start, end, }: { - esClient: ElasticsearchClient; - indexName?: string; + infraMetricsClient: InfraMetricsClient; containerId: string; start: number; end: number; }): Promise => { - if (!indexName) { - return undefined; - } - const should = [ { exists: { field: KUBERNETES } }, { exists: { field: CONTAINER_IMAGE } }, @@ -56,9 +49,7 @@ export const getServiceInstanceContainerMetadata = async ({ { exists: { field: KUBERNETES_DEPLOYMENT_NAME } }, ]; - const response = await esClient.search({ - index: [indexName], - _source: [KUBERNETES, CONTAINER], + const response = await infraMetricsClient.search({ size: 1, query: { bool: { diff --git a/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts b/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts index 5b267e4faa3f..c1dcfe97e208 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_overview_container_metadata.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core/server'; import { rangeQuery } from '@kbn/observability-plugin/server'; import { - CONTAINER, CONTAINER_ID, CONTAINER_IMAGE, KUBERNETES, @@ -19,43 +17,19 @@ import { KUBERNETES_REPLICASET_NAME, KUBERNETES_DEPLOYMENT_NAME, } from '../../../common/elasticsearch_fieldnames'; - -type ServiceOverviewContainerMetadataDetails = - | { - kubernetes: { - deployments?: string[]; - replicasets?: string[]; - namespaces?: string[]; - containerImages?: string[]; - }; - } - | undefined; - -interface ResponseAggregations { - [key: string]: { - buckets: Array<{ - key: string; - }>; - }; -} +import { InfraMetricsClient } from '../../lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client'; export const getServiceOverviewContainerMetadata = async ({ - esClient, - indexName, + infraMetricsClient, containerIds, start, end, }: { - esClient: ElasticsearchClient; - indexName?: string; + infraMetricsClient: InfraMetricsClient; containerIds: string[]; start: number; end: number; -}): Promise => { - if (!indexName) { - return undefined; - } - +}) => { const should = [ { exists: { field: KUBERNETES } }, { exists: { field: CONTAINER_IMAGE } }, @@ -67,9 +41,7 @@ export const getServiceOverviewContainerMetadata = async ({ { exists: { field: KUBERNETES_DEPLOYMENT_NAME } }, ]; - const response = await esClient.search({ - index: [indexName], - _source: [KUBERNETES, CONTAINER], + const response = await infraMetricsClient.search({ size: 0, query: { bool: { diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 6b73d72367d0..79ff09101aec 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -55,7 +55,8 @@ import { ServiceHealthStatus } from '../../../common/service_health_status'; import { getServiceGroup } from '../service_groups/get_service_group'; import { offsetRt } from '../../../common/comparison_rt'; import { getRandomSampler } from '../../lib/helpers/get_random_sampler'; -import { getInfraMetricIndices } from '../../lib/helpers/get_infra_metric_indices'; +import { createInfraMetricsClient } from '../../lib/helpers/create_es_client/create_infra_metrics_client/create_infra_metrics_client'; + const servicesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services', params: t.type({ @@ -274,7 +275,8 @@ const serviceMetadataDetailsRoute = createApmServerRoute({ import('./get_service_metadata_details').ServiceMetadataDetails > => { const setup = await setupRequest(resources); - const { params, context, plugins } = resources; + const infraMetricsClient = createInfraMetricsClient(resources); + const { params } = resources; const { serviceName } = params.path; const { start, end } = params.query; @@ -295,19 +297,8 @@ const serviceMetadataDetailsRoute = createApmServerRoute({ }); if (serviceMetadataDetails?.container?.ids) { - const { - savedObjects: { client: savedObjectsClient }, - elasticsearch: { client: esClient }, - } = await context.core; - - const indexName = await getInfraMetricIndices({ - infraPlugin: plugins.infra, - savedObjectsClient, - }); - const containerMetadata = await getServiceOverviewContainerMetadata({ - esClient: esClient.asCurrentUser, - indexName, + infraMetricsClient, containerIds: serviceMetadataDetails.container.ids, start, end, @@ -911,7 +902,8 @@ export const serviceInstancesMetadataDetails = createApmServerRoute({ | undefined; }> => { const setup = await setupRequest(resources); - const { params, context, plugins } = resources; + const infraMetricsClient = createInfraMetricsClient(resources); + const { params } = resources; const { serviceName, serviceNodeName } = params.path; const { start, end } = params.query; @@ -925,19 +917,8 @@ export const serviceInstancesMetadataDetails = createApmServerRoute({ }); if (serviceInstanceMetadataDetails?.container?.id) { - const { - savedObjects: { client: savedObjectsClient }, - elasticsearch: { client: esClient }, - } = await context.core; - - const indexName = await getInfraMetricIndices({ - infraPlugin: plugins.infra, - savedObjectsClient, - }); - const containerMetadata = await getServiceInstanceContainerMetadata({ - esClient: esClient.asCurrentUser, - indexName, + infraMetricsClient, containerId: serviceInstanceMetadataDetails.container.id, start, end, From 94fe1e63530d73807fca991898c8caf42e5d4dca Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 29 Sep 2022 13:39:14 +0300 Subject: [PATCH 108/185] [Visualizations] Navigate to lens agg based vis library tests. (#141353) * Added tests for convert_to_lens lib at vis_types. Co-authored-by: Uladzislau Lasitsa --- .../convert_to_lens/lib/buckets/index.test.ts | 267 +++++++++ .../lib/convert/column.test.ts | 105 ++++ .../lib/convert/date_histogram.test.ts | 90 +++ .../lib/convert/filters.test.ts | 54 ++ .../lib/convert/formula.test.ts | 54 ++ .../lib/convert/last_value.test.ts | 120 ++++ .../lib/convert/metric.test.ts | 92 ++++ .../lib/convert/parent_pipeline.test.ts | 438 +++++++++++++++ .../lib/convert/parent_pipeline.ts | 3 +- .../lib/convert/percentile.test.ts | 142 +++++ .../lib/convert/percentile_rank.test.ts | 146 +++++ .../convert_to_lens/lib/convert/range.test.ts | 74 +++ .../lib/convert/sibling_pipeline.test.ts | 79 +++ .../lib/convert/std_deviation.test.ts | 115 ++++ .../convert_to_lens/lib/convert/terms.test.ts | 241 ++++++++ .../lib/metrics/formula.test.ts | 474 ++++++++++++++++ .../lib/metrics/metrics.test.ts | 368 +++++++++++++ .../lib/metrics/percentage_formula.test.ts | 98 ++++ .../common/convert_to_lens/lib/utils.test.ts | 521 ++++++++++++++++++ .../common/convert_to_lens/lib/utils.ts | 2 +- .../public/convert_to_lens/schemas.test.ts | 2 +- .../public/convert_to_lens/schemas.ts | 4 +- 22 files changed, 3484 insertions(+), 5 deletions(-) create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/buckets/index.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/column.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/date_histogram.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/filters.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/formula.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/last_value.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/metric.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile_rank.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/range.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/sibling_pipeline.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/std_deviation.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/convert/terms.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/metrics/formula.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/metrics/percentage_formula.test.ts create mode 100644 src/plugins/visualizations/common/convert_to_lens/lib/utils.test.ts diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/buckets/index.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/buckets/index.test.ts new file mode 100644 index 000000000000..f0a8e4d32f7c --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/buckets/index.test.ts @@ -0,0 +1,267 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { BUCKET_TYPES, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { convertBucketToColumns } from '.'; +import { DateHistogramColumn, FiltersColumn, RangeColumn, TermsColumn } from '../../types'; +import { AggBasedColumn, SchemaConfig } from '../../..'; + +const mockConvertToDateHistogramColumn = jest.fn(); +const mockConvertToFiltersColumn = jest.fn(); +const mockConvertToTermsColumn = jest.fn(); +const mockConvertToRangeColumn = jest.fn(); + +jest.mock('../convert', () => ({ + convertToDateHistogramColumn: jest.fn(() => mockConvertToDateHistogramColumn()), + convertToFiltersColumn: jest.fn(() => mockConvertToFiltersColumn()), + convertToTermsColumn: jest.fn(() => mockConvertToTermsColumn()), + convertToRangeColumn: jest.fn(() => mockConvertToRangeColumn()), +})); + +describe('convertBucketToColumns', () => { + const field = stubLogstashDataView.fields[0].name; + const dateField = stubLogstashDataView.fields.find((f) => f.type === 'date')!.name; + const bucketAggs: SchemaConfig[] = [ + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.FILTERS, + aggParams: { + filters: [], + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.DATE_HISTOGRAM, + aggParams: { + field, + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.TERMS, + aggParams: { + field, + orderBy: '_key', + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.TERMS, + aggParams: { + field: dateField, + orderBy: '_key', + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.HISTOGRAM, + aggParams: { + field, + interval: '1h', + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.RANGE, + aggParams: { + field, + }, + }, + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: BUCKET_TYPES.DATE_RANGE, + aggParams: { + field, + }, + }, + ]; + const aggs: Array> = [ + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + }, + ]; + const metricColumns: AggBasedColumn[] = [ + { + columnId: 'column-1', + operationType: 'average', + isBucketed: false, + isSplit: false, + sourceField: field, + dataType: 'number', + params: {}, + meta: { + aggId: '1', + }, + }, + ]; + + afterEach(() => { + jest.clearAllMocks(); + }); + + test.each< + [ + string, + Parameters, + () => void, + Partial | null + ] + >([ + [ + 'null if bucket agg type is not supported', + [{ dataView: stubLogstashDataView, agg: bucketAggs[6], aggs, metricColumns }], + () => {}, + null, + ], + [ + 'null if bucket agg does not have aggParams', + [ + { + dataView: stubLogstashDataView, + agg: { ...bucketAggs[0], aggParams: undefined }, + aggs, + metricColumns, + }, + ], + () => {}, + null, + ], + [ + 'filters column if bucket agg is valid filters agg', + [{ dataView: stubLogstashDataView, agg: bucketAggs[0], aggs, metricColumns }], + () => { + mockConvertToFiltersColumn.mockReturnValue({ + operationType: 'filters', + }); + }, + { + operationType: 'filters', + }, + ], + [ + 'date histogram column if bucket agg is valid date histogram agg', + [{ dataView: stubLogstashDataView, agg: bucketAggs[1], aggs, metricColumns }], + () => { + mockConvertToDateHistogramColumn.mockReturnValue({ + operationType: 'date_histogram', + }); + }, + { + operationType: 'date_histogram', + }, + ], + [ + 'date histogram column if bucket agg is valid terms agg with date field', + [{ dataView: stubLogstashDataView, agg: bucketAggs[3], aggs, metricColumns }], + () => { + mockConvertToDateHistogramColumn.mockReturnValue({ + operationType: 'date_histogram', + }); + }, + { + operationType: 'date_histogram', + }, + ], + [ + 'terms column if bucket agg is valid terms agg with no date field', + [{ dataView: stubLogstashDataView, agg: bucketAggs[2], aggs, metricColumns }], + () => { + mockConvertToTermsColumn.mockReturnValue({ + operationType: 'terms', + }); + }, + { + operationType: 'terms', + }, + ], + [ + 'range column if bucket agg is valid histogram agg', + [{ dataView: stubLogstashDataView, agg: bucketAggs[4], aggs, metricColumns }], + () => { + mockConvertToRangeColumn.mockReturnValue({ + operationType: 'range', + }); + }, + { + operationType: 'range', + }, + ], + [ + 'range column if bucket agg is valid range agg', + [{ dataView: stubLogstashDataView, agg: bucketAggs[5], aggs, metricColumns }], + () => { + mockConvertToRangeColumn.mockReturnValue({ + operationType: 'range', + }); + }, + { + operationType: 'range', + }, + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(convertBucketToColumns(...input)).toBeNull(); + } else { + expect(convertBucketToColumns(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.test.ts new file mode 100644 index 000000000000..74e9f2a57a9f --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/column.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/public'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { createColumn } from './column'; +import { GeneralColumnWithMeta } from './types'; + +describe('createColumn', () => { + const field = stubLogstashDataView.fields[0]; + const aggId = `some-id`; + const customLabel = 'some-custom-label'; + const label = 'some label'; + const timeShift = '1h'; + + const agg: SchemaConfig = { + accessor: 0, + label, + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggId, + aggParams: { + field: field.name, + }, + }; + + const aggWithCustomLabel: SchemaConfig = { + ...agg, + aggParams: { + field: field.name, + customLabel, + }, + }; + + const aggWithTimeShift: SchemaConfig = { + ...agg, + aggParams: { + field: field.name, + timeShift, + }, + }; + + const extraColumnFields = { isBucketed: true, isSplit: true, reducedTimeRange: '1m' }; + + test.each<[string, Parameters, Partial]>([ + [ + 'with default params', + [agg, field], + { + dataType: 'number', + isBucketed: false, + isSplit: false, + label, + meta: { aggId }, + }, + ], + [ + 'with custom label', + [aggWithCustomLabel, field], + { + dataType: 'number', + isBucketed: false, + isSplit: false, + label: customLabel, + meta: { aggId }, + }, + ], + [ + 'with timeShift', + [aggWithTimeShift, field], + { + dataType: 'number', + isBucketed: false, + isSplit: false, + label, + meta: { aggId }, + timeShift, + }, + ], + [ + 'with extra column fields', + [agg, field, extraColumnFields], + { + dataType: 'number', + isBucketed: extraColumnFields.isBucketed, + isSplit: extraColumnFields.isSplit, + reducedTimeRange: extraColumnFields.reducedTimeRange, + label, + meta: { aggId }, + }, + ], + ])('should create column by agg %s', (_, input, expected) => { + expect(createColumn(...input)).toEqual(expect.objectContaining(expected)); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/date_histogram.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/date_histogram.test.ts new file mode 100644 index 000000000000..897e5f876542 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/date_histogram.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { AggParamsDateHistogram } from '@kbn/data-plugin/common'; +import { convertToDateHistogramColumn } from './date_histogram'; +import { DateHistogramColumn } from './types'; +import { DataType } from '../../types'; + +describe('convertToDateHistogramColumn', () => { + const aggId = `some-id`; + const timeShift = '1h'; + const aggParams: AggParamsDateHistogram = { + interval: '1d', + drop_partials: true, + field: stubLogstashDataView.fields[0].name, + }; + + test.each< + [string, Parameters, Partial | null] + >([ + [ + 'date histogram column if field is provided', + [aggId, aggParams, stubLogstashDataView, false, false], + { + dataType: stubLogstashDataView.fields[0].type as DataType, + isBucketed: true, + isSplit: false, + timeShift: undefined, + sourceField: stubLogstashDataView.fields[0].name, + meta: { aggId }, + params: { + interval: '1d', + includeEmptyRows: true, + dropPartials: true, + }, + }, + ], + [ + 'null if field is not provided', + [aggId, { interval: '1d', field: undefined }, stubLogstashDataView, false, false], + null, + ], + [ + 'date histogram column with isSplit and timeShift if specified', + [aggId, { ...aggParams, timeShift }, stubLogstashDataView, true, false], + { + dataType: stubLogstashDataView.fields[0].type as DataType, + isBucketed: true, + isSplit: true, + sourceField: stubLogstashDataView.fields[0].name, + timeShift, + meta: { aggId }, + params: { + interval: '1d', + includeEmptyRows: true, + dropPartials: true, + }, + }, + ], + [ + 'date histogram column with dropEmptyRowsInDateHistogram if specified', + [aggId, aggParams, stubLogstashDataView, true, true], + { + dataType: stubLogstashDataView.fields[0].type as DataType, + isBucketed: true, + isSplit: true, + sourceField: stubLogstashDataView.fields[0].name, + timeShift: undefined, + meta: { aggId }, + params: { + interval: '1d', + includeEmptyRows: false, + dropPartials: true, + }, + }, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToDateHistogramColumn(...input)).toBeNull(); + } else { + expect(convertToDateHistogramColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/filters.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/filters.test.ts new file mode 100644 index 000000000000..1de1dbc9b312 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/filters.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { AggParamsFilters } from '@kbn/data-plugin/common'; +import { convertToFiltersColumn } from './filters'; +import { FiltersColumn } from './types'; + +describe('convertToFiltersColumn', () => { + const aggId = `some-id`; + const timeShift = '1h'; + const filters = [{ input: { language: 'lucene', query: 'some other query' }, label: 'split' }]; + const aggParams: AggParamsFilters = { + filters, + }; + + test.each<[string, Parameters, Partial | null]>([ + [ + 'filters column if filters are provided', + [aggId, aggParams], + { + dataType: 'string', + isBucketed: true, + isSplit: false, + timeShift: undefined, + meta: { aggId }, + params: { filters: aggParams.filters! }, + }, + ], + ['null if filters are not provided', [aggId, {}], null], + [ + 'filters column with isSplit and timeShift if specified', + [aggId, { ...aggParams, timeShift }, true], + { + dataType: 'string', + isBucketed: true, + isSplit: true, + timeShift, + meta: { aggId }, + params: { filters: aggParams.filters! }, + }, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToFiltersColumn(...input)).toBeNull(); + } else { + expect(convertToFiltersColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/formula.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/formula.test.ts new file mode 100644 index 000000000000..bbde6a04f1a2 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/formula.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IAggConfig, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { createFormulaColumn } from './formula'; + +describe('createFormulaColumn', () => { + const aggId = `some-id`; + const label = 'some label'; + const agg: SchemaConfig = { + accessor: 0, + label, + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.CUMULATIVE_SUM, + aggId, + aggParams: { + customMetric: { + id: 'some-id-metric', + enabled: true, + type: { name: METRIC_TYPES.AVG }, + params: { + field: stubLogstashDataView.fields[0], + }, + } as IAggConfig, + }, + }; + test('should return formula column', () => { + expect(createFormulaColumn('test-formula', agg)).toEqual( + expect.objectContaining({ + isBucketed: false, + isSplit: false, + meta: { + aggId, + }, + operationType: 'formula', + params: { + formula: 'test-formula', + }, + references: [], + }) + ); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/last_value.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/last_value.test.ts new file mode 100644 index 000000000000..55ba1e8b5e09 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/last_value.test.ts @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { AggParamsTopHit, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToLastValueColumn } from './last_value'; +import { FiltersColumn } from './types'; + +const mockGetFieldNameFromField = jest.fn(); +const mockGetFieldByName = jest.fn(); +const mockGetLabel = jest.fn(); + +jest.mock('../utils', () => ({ + getFieldNameFromField: jest.fn(() => mockGetFieldNameFromField()), + getLabel: jest.fn(() => mockGetLabel()), +})); + +describe('convertToLastValueColumn', () => { + const dataView = stubLogstashDataView; + const sortField = dataView.fields[0]; + + const topHitAggParams: AggParamsTopHit = { + sortOrder: { + value: 'desc', + text: 'some text', + }, + sortField, + field: '', + aggregate: 'min', + size: 1, + }; + + const topHitAgg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.TOP_HITS, + aggParams: topHitAggParams, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFieldNameFromField.mockReturnValue(dataView.fields[0]); + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + mockGetLabel.mockReturnValue('someLabel'); + dataView.getFieldByName = mockGetFieldByName; + }); + + test.each<[string, Parameters, Partial | null]>([ + [ + 'null if top hits size is more than 1', + [{ agg: { ...topHitAgg, aggParams: { ...topHitAgg.aggParams!, size: 2 } }, dataView }], + null, + ], + [ + 'null if top hits sord order is not desc', + [ + { + agg: { + ...topHitAgg, + aggParams: { + ...topHitAgg.aggParams!, + sortOrder: { ...topHitAgg.aggParams!.sortOrder!, value: 'asc' }, + }, + }, + dataView, + }, + ], + null, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToLastValueColumn(...input)).toBeNull(); + } else { + expect(convertToLastValueColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); + + test('should skip if top hit field is not specified', () => { + mockGetFieldNameFromField.mockReturnValue(null); + expect(convertToLastValueColumn({ agg: topHitAgg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(0); + }); + + test('should skip if top hit field is not present in index pattern', () => { + mockGetFieldByName.mockReturnValue(null); + dataView.getFieldByName = mockGetFieldByName; + + expect(convertToLastValueColumn({ agg: topHitAgg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + expect(mockGetLabel).toBeCalledTimes(0); + }); + + test('should return top hit column if top hit field is not present in index pattern', () => { + expect(convertToLastValueColumn({ agg: topHitAgg, dataView })).toEqual( + expect.objectContaining({ + dataType: 'number', + label: 'someLabel', + operationType: 'last_value', + params: { showArrayValues: true, sortField: 'bytes' }, + sourceField: 'bytes', + }) + ); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + expect(mockGetLabel).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/metric.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/metric.test.ts new file mode 100644 index 000000000000..3be17abc46ac --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/metric.test.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertMetricAggregationColumnWithoutSpecialParams } from './metric'; +import { SUPPORTED_METRICS } from './supported_metrics'; + +const mockGetFieldByName = jest.fn(); + +describe('convertToLastValueColumn', () => { + const dataView = stubLogstashDataView; + + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggParams: { + field: dataView.fields[0].displayName, + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + dataView.getFieldByName = mockGetFieldByName; + }); + + test('should return null metric is not supported', () => { + expect( + convertMetricAggregationColumnWithoutSpecialParams(SUPPORTED_METRICS[METRIC_TYPES.TOP_HITS], { + agg, + dataView, + }) + ).toBeNull(); + }); + + test('should skip if field is not present and is required for the aggregation', () => { + mockGetFieldByName.mockReturnValue(null); + dataView.getFieldByName = mockGetFieldByName; + + expect( + convertMetricAggregationColumnWithoutSpecialParams(SUPPORTED_METRICS[METRIC_TYPES.AVG], { + agg, + dataView, + }) + ).toBeNull(); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return column if field is not present and is not required for the aggregation', () => { + mockGetFieldByName.mockReturnValue(null); + dataView.getFieldByName = mockGetFieldByName; + + expect( + convertMetricAggregationColumnWithoutSpecialParams(SUPPORTED_METRICS[METRIC_TYPES.COUNT], { + agg, + dataView, + }) + ).toEqual(expect.objectContaining({ operationType: 'count' })); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return column if field is present and is required for the aggregation', () => { + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + dataView.getFieldByName = mockGetFieldByName; + + expect( + convertMetricAggregationColumnWithoutSpecialParams(SUPPORTED_METRICS[METRIC_TYPES.AVG], { + agg, + dataView, + }) + ).toEqual( + expect.objectContaining({ + dataType: 'number', + operationType: 'average', + }) + ); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.test.ts new file mode 100644 index 000000000000..c28324533c83 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.test.ts @@ -0,0 +1,438 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { FormulaColumn, AggBasedColumn } from './types'; +import { SchemaConfig } from '../../..'; +import { + convertToOtherParentPipelineAggColumns, + ParentPipelineAggColumn, + convertToCumulativeSumAggColumn, +} from './parent_pipeline'; + +const mockGetMetricFromParentPipelineAgg = jest.fn(); +const mockGetFormulaForPipelineAgg = jest.fn(); +const mockConvertMetricToColumns = jest.fn(); +const mockGetFieldByName = jest.fn(); +const mockConvertMetricAggregationColumnWithoutSpecialParams = jest.fn(); + +jest.mock('../utils', () => ({ + getMetricFromParentPipelineAgg: jest.fn(() => mockGetMetricFromParentPipelineAgg()), + getLabel: jest.fn(() => 'label'), + getFieldNameFromField: jest.fn(() => 'document'), +})); + +jest.mock('./metric', () => ({ + convertMetricAggregationColumnWithoutSpecialParams: jest.fn(() => + mockConvertMetricAggregationColumnWithoutSpecialParams() + ), +})); + +jest.mock('../metrics', () => ({ + getFormulaForPipelineAgg: jest.fn(() => mockGetFormulaForPipelineAgg()), + convertMetricToColumns: jest.fn(() => mockConvertMetricToColumns()), +})); + +describe('convertToOtherParentPipelineAggColumns', () => { + const field = stubLogstashDataView.fields[0].name; + const aggs: Array> = [ + { + aggId: '1', + aggType: METRIC_TYPES.AVG, + aggParams: { field }, + accessor: 0, + params: {}, + label: 'average', + format: {}, + }, + { + aggId: '1', + aggType: METRIC_TYPES.MOVING_FN, + aggParams: { metricAgg: '2' }, + accessor: 0, + params: {}, + label: 'Moving Average of Average', + format: {}, + }, + ]; + + afterEach(() => { + jest.clearAllMocks(); + }); + + test.each< + [ + string, + Parameters, + () => void, + Partial | [Partial, Partial] | null + ] + >([ + [ + 'null if getMetricFromParentPipelineAgg returns null', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'null if cutom metric of parent pipeline agg is not supported', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.GEO_BOUNDS, + }); + }, + null, + ], + [ + 'null if cutom metric of parent pipeline agg is sibling pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.AVG_BUCKET, + }); + }, + null, + ], + [ + 'null if cannot build formula if cutom metric of parent pipeline agg is parent pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.MOVING_FN, + }); + mockGetFormulaForPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'formula column if cutom metric of parent pipeline agg is valid parent pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.MOVING_FN, + }); + mockGetFormulaForPipelineAgg.mockReturnValue('test-formula'); + }, + { + operationType: 'formula', + params: { + formula: 'test-formula', + }, + }, + ], + [ + 'null if cutom metric of parent pipeline agg is invalid not pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.AVG, + }); + mockConvertMetricToColumns.mockReturnValue(null); + }, + null, + ], + [ + 'parent pipeline and metric columns if cutom metric of parent pipeline agg is valid not pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.AVG, + }); + mockConvertMetricToColumns.mockReturnValue([ + { + columnId: 'test-id-1', + operationType: 'average', + sourceField: field, + }, + ]); + }, + [ + { operationType: 'moving_average', references: ['test-id-1'] }, + { + columnId: 'test-id-1', + operationType: 'average', + sourceField: field, + }, + ], + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(convertToOtherParentPipelineAggColumns(...input)).toBeNull(); + } else if (Array.isArray(expected)) { + expect(convertToOtherParentPipelineAggColumns(...input)).toEqual( + expected.map(expect.objectContaining) + ); + } else { + expect(convertToOtherParentPipelineAggColumns(...input)).toEqual( + expect.objectContaining(expected) + ); + } + }); +}); + +describe('convertToCumulativeSumAggColumn', () => { + const field = stubLogstashDataView.fields[0].name; + const aggs: Array> = [ + { + aggId: '1', + aggType: METRIC_TYPES.AVG, + aggParams: { field }, + accessor: 0, + params: {}, + label: 'average', + format: {}, + }, + { + aggId: '1', + aggType: METRIC_TYPES.CUMULATIVE_SUM, + aggParams: { metricAgg: '2' }, + accessor: 0, + params: {}, + label: 'Moving Average of Average', + format: {}, + }, + ]; + + beforeEach(() => { + mockGetFieldByName.mockReturnValue({ + aggregatable: true, + type: 'number', + sourceField: 'bytes', + }); + + stubLogstashDataView.getFieldByName = mockGetFieldByName; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test.each< + [ + string, + Parameters, + () => void, + Partial | [Partial, Partial] | null + ] + >([ + [ + 'null if cumulative sum does not have aggParams', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: { ...aggs[1], aggParams: undefined } as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'null if getMetricFromParentPipelineAgg returns null', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'null if cutom metric of parent pipeline agg is not supported', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.GEO_BOUNDS, + }); + }, + null, + ], + [ + 'null if cutom metric of parent pipeline agg is sibling pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.AVG_BUCKET, + }); + }, + null, + ], + [ + 'null if cannot build formula if cutom metric of parent pipeline agg is parent pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.MOVING_FN, + }); + mockGetFormulaForPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'formula column if cutom metric of parent pipeline agg is valid parent pipeline agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.MOVING_FN, + }); + mockGetFormulaForPipelineAgg.mockReturnValue('test-formula'); + }, + { + operationType: 'formula', + params: { + formula: 'test-formula', + }, + }, + ], + [ + 'null if cutom metric of parent pipeline agg is invalid sum or count agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.SUM, + }); + mockConvertMetricAggregationColumnWithoutSpecialParams.mockReturnValue(null); + }, + null, + ], + [ + 'cumulative sum and metric columns if cutom metric of parent pipeline agg is valid sum or count agg', + [ + { + dataView: stubLogstashDataView, + aggs, + agg: aggs[1] as SchemaConfig, + }, + ], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggId: '2-metric', + aggType: METRIC_TYPES.SUM, + }); + mockConvertMetricAggregationColumnWithoutSpecialParams.mockReturnValue({ + columnId: 'test-id-1', + operationType: 'sum', + sourceField: field, + }); + }, + [ + { operationType: 'cumulative_sum', references: ['test-id-1'] }, + { + columnId: 'test-id-1', + operationType: 'sum', + sourceField: field, + }, + ], + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(convertToCumulativeSumAggColumn(...input)).toBeNull(); + } else if (Array.isArray(expected)) { + expect(convertToCumulativeSumAggColumn(...input)).toEqual( + expected.map(expect.objectContaining) + ); + } else { + expect(convertToCumulativeSumAggColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.ts index c1fd75ae1926..ab41ceb259ad 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/parent_pipeline.ts @@ -122,6 +122,7 @@ export const convertToCumulativeSumAggColumn = ( { agg: metric as SchemaConfig, dataView }, reducedTimeRange ); + if (subMetric === null) { return null; } @@ -134,8 +135,8 @@ export const convertToCumulativeSumAggColumn = ( return [ { operationType: op.name, - references: [subMetric?.columnId], ...createColumn(agg), + references: [subMetric?.columnId], params: {}, timeShift: agg.aggParams?.timeShift, } as ParentPipelineAggColumn, diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile.test.ts new file mode 100644 index 000000000000..b4cf7f141e92 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToPercentileColumn } from './percentile'; +import { PercentileColumn } from './types'; + +const mockGetFieldNameFromField = jest.fn(); +const mockGetFieldByName = jest.fn(); +const mockGetLabel = jest.fn(); +const mockGetLabelForPercentile = jest.fn(); + +jest.mock('../utils', () => ({ + getFieldNameFromField: jest.fn(() => mockGetFieldNameFromField()), + getLabel: jest.fn(() => mockGetLabel()), + getLabelForPercentile: jest.fn(() => mockGetLabelForPercentile()), +})); + +describe('convertToPercentileColumn', () => { + const dataView = stubLogstashDataView; + const field = dataView.fields[0].displayName; + const aggId = 'pr.10'; + const percentile = 10; + const percents = [percentile]; + + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.PERCENTILES, + aggParams: { field, percents }, + aggId, + }; + const singlePercentileRankAgg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.SINGLE_PERCENTILE, + aggParams: { field, percentile }, + aggId, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFieldNameFromField.mockReturnValue(dataView.fields[0]); + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + mockGetLabel.mockReturnValue('someLabel'); + mockGetLabelForPercentile.mockReturnValue('someOtherLabel'); + dataView.getFieldByName = mockGetFieldByName; + }); + + test.each< + [string, Parameters, Partial | null] + >([ + ['null if no percents', [{ agg: { ...agg, aggId: 'pr' }, dataView }], null], + [ + 'null if no value', + [{ agg: { ...singlePercentileRankAgg, aggParams: undefined }, dataView }], + null, + ], + ['null if no aggId', [{ agg: { ...agg, aggId: undefined }, dataView }], null], + ['null if no aggParams', [{ agg: { ...agg, aggParams: undefined }, dataView }], null], + ['null if aggId is invalid', [{ agg: { ...agg, aggId: 'pr.invalid' }, dataView }], null], + [ + 'null if values are undefined', + [{ agg: { ...agg, aggParams: { percents: undefined, field } }, dataView }], + null, + ], + [ + 'null if values are empty', + [{ agg: { ...agg, aggParams: { percents: [], field } }, dataView }], + null, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToPercentileColumn(...input)).toBeNull(); + } else { + expect(convertToPercentileColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); + + test('should return null if field is not specified', () => { + mockGetFieldNameFromField.mockReturnValue(null); + expect(convertToPercentileColumn({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(0); + }); + + test('should return null if field absent at the index pattern', () => { + mockGetFieldByName.mockReturnValueOnce(null); + dataView.getFieldByName = mockGetFieldByName; + + expect(convertToPercentileColumn({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return percentile rank column for percentiles', () => { + expect(convertToPercentileColumn({ agg, dataView })).toEqual( + expect.objectContaining({ + dataType: 'number', + label: 'someOtherLabel', + meta: { aggId: 'pr.10' }, + operationType: 'percentile', + params: { percentile: 10 }, + sourceField: 'bytes', + }) + ); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return percentile rank column for single percentile', () => { + expect(convertToPercentileColumn({ agg: singlePercentileRankAgg, dataView })).toEqual( + expect.objectContaining({ + dataType: 'number', + label: 'someOtherLabel', + meta: { aggId: 'pr.10' }, + operationType: 'percentile', + params: { percentile: 10 }, + sourceField: 'bytes', + }) + ); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile_rank.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile_rank.test.ts new file mode 100644 index 000000000000..8a696d51d871 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/percentile_rank.test.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToPercentileRankColumn } from './percentile_rank'; +import { PercentileRanksColumn } from './types'; + +const mockGetFieldNameFromField = jest.fn(); +const mockGetFieldByName = jest.fn(); +const mockGetLabel = jest.fn(); +const mockGetLabelForPercentile = jest.fn(); + +jest.mock('../utils', () => ({ + getFieldNameFromField: jest.fn(() => mockGetFieldNameFromField()), + getLabel: jest.fn(() => mockGetLabel()), + getLabelForPercentile: jest.fn(() => mockGetLabelForPercentile()), +})); + +describe('convertToPercentileRankColumn', () => { + const dataView = stubLogstashDataView; + const field = dataView.fields[0].displayName; + const aggId = 'pr.10'; + const value = 10; + const values = [value]; + + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.PERCENTILE_RANKS, + aggParams: { field, values }, + aggId, + }; + const singlePercentileRankAgg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK, + aggParams: { field, value }, + aggId, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFieldNameFromField.mockReturnValue(dataView.fields[0]); + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + mockGetLabel.mockReturnValue('someLabel'); + mockGetLabelForPercentile.mockReturnValue('someOtherLabel'); + dataView.getFieldByName = mockGetFieldByName; + }); + + test.each< + [ + string, + Parameters, + Partial | null + ] + >([ + ['null if no percents', [{ agg: { ...agg, aggId: 'pr' }, dataView }], null], + [ + 'null if no value', + [{ agg: { ...singlePercentileRankAgg, aggParams: undefined }, dataView }], + null, + ], + ['null if no aggId', [{ agg: { ...agg, aggId: undefined }, dataView }], null], + ['null if no aggParams', [{ agg: { ...agg, aggParams: undefined }, dataView }], null], + ['null if aggId is invalid', [{ agg: { ...agg, aggId: 'pr.invalid' }, dataView }], null], + [ + 'null if values are undefined', + [{ agg: { ...agg, aggParams: { values: undefined, field } }, dataView }], + null, + ], + [ + 'null if values are empty', + [{ agg: { ...agg, aggParams: { values: [], field } }, dataView }], + null, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToPercentileRankColumn(...input)).toBeNull(); + } else { + expect(convertToPercentileRankColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); + + test('should return null if field is not specified', () => { + mockGetFieldNameFromField.mockReturnValue(null); + expect(convertToPercentileRankColumn({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(0); + }); + + test('should return null if field absent at the index pattern', () => { + mockGetFieldByName.mockReturnValueOnce(null); + dataView.getFieldByName = mockGetFieldByName; + + expect(convertToPercentileRankColumn({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return percentile rank column for percentile ranks', () => { + expect(convertToPercentileRankColumn({ agg, dataView })).toEqual( + expect.objectContaining({ + dataType: 'number', + label: 'someOtherLabel', + meta: { aggId: 'pr.10' }, + operationType: 'percentile_rank', + params: { value: 10 }, + sourceField: 'bytes', + }) + ); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return percentile rank column for single percentile rank', () => { + expect(convertToPercentileRankColumn({ agg: singlePercentileRankAgg, dataView })).toEqual( + expect.objectContaining({ + dataType: 'number', + label: 'someOtherLabel', + meta: { aggId: 'pr.10' }, + operationType: 'percentile_rank', + params: { value: 10 }, + sourceField: 'bytes', + }) + ); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/range.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/range.test.ts new file mode 100644 index 000000000000..8f535c28c826 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/range.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { AggParamsRange, AggParamsHistogram } from '@kbn/data-plugin/common'; +import { convertToRangeColumn } from './range'; +import { RangeColumn } from './types'; +import { DataType } from '../../types'; +import { RANGE_MODES } from '../../constants'; + +describe('convertToRangeColumn', () => { + const aggId = `some-id`; + const ranges = [ + { + from: 1, + to: 1000, + label: '1', + }, + ]; + const aggParamsRange: AggParamsRange = { + field: stubLogstashDataView.fields[0].name, + ranges, + }; + const aggParamsHistogram: AggParamsHistogram = { + interval: '1d', + field: stubLogstashDataView.fields[0].name, + }; + + test.each<[string, Parameters, Partial | null]>([ + [ + 'range column if provide valid range agg', + [aggId, aggParamsRange, '', stubLogstashDataView], + { + dataType: stubLogstashDataView.fields[0].type as DataType, + isBucketed: true, + isSplit: false, + sourceField: stubLogstashDataView.fields[0].name, + meta: { aggId }, + params: { + type: RANGE_MODES.Range, + maxBars: 'auto', + ranges, + }, + }, + ], + [ + 'range column if provide valid histogram agg', + [aggId, aggParamsHistogram, '', stubLogstashDataView, true], + { + dataType: stubLogstashDataView.fields[0].type as DataType, + isBucketed: true, + isSplit: true, + sourceField: stubLogstashDataView.fields[0].name, + meta: { aggId }, + params: { + type: RANGE_MODES.Histogram, + maxBars: 'auto', + ranges: [], + }, + }, + ], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(convertToRangeColumn(...input)).toBeNull(); + } else { + expect(convertToRangeColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/sibling_pipeline.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/sibling_pipeline.test.ts new file mode 100644 index 000000000000..759620650b8a --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/sibling_pipeline.test.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IAggConfig, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToSiblingPipelineColumns } from './sibling_pipeline'; + +const mockConvertMetricToColumns = jest.fn(); +const mockConvertToSchemaConfig = jest.fn(); + +jest.mock('../metrics', () => ({ + convertMetricToColumns: jest.fn(() => mockConvertMetricToColumns()), +})); + +jest.mock('../../../vis_schemas', () => ({ + convertToSchemaConfig: jest.fn(() => mockConvertToSchemaConfig()), +})); + +describe('convertToSiblingPipelineColumns', () => { + const dataView = stubLogstashDataView; + const aggId = 'agg-id-1'; + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG_BUCKET, + aggParams: { customMetric: {} as IAggConfig }, + aggId, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockConvertMetricToColumns.mockReturnValue([{}]); + mockConvertToSchemaConfig.mockReturnValue({}); + }); + + test('should return null if aggParams are not defined', () => { + expect( + convertToSiblingPipelineColumns({ agg: { ...agg, aggParams: undefined }, aggs: [], dataView }) + ).toBeNull(); + expect(mockConvertMetricToColumns).toBeCalledTimes(0); + }); + + test('should return null if customMetric is not defined', () => { + expect( + convertToSiblingPipelineColumns({ + agg: { ...agg, aggParams: { customMetric: undefined } }, + aggs: [], + dataView, + }) + ).toBeNull(); + expect(mockConvertMetricToColumns).toBeCalledTimes(0); + }); + + test('should return null if sibling agg is not supported', () => { + mockConvertMetricToColumns.mockReturnValue(null); + expect(convertToSiblingPipelineColumns({ agg, aggs: [], dataView })).toBeNull(); + expect(mockConvertToSchemaConfig).toBeCalledTimes(1); + expect(mockConvertMetricToColumns).toBeCalledTimes(1); + }); + + test('should return column', () => { + const column = { operationType: 'formula' }; + mockConvertMetricToColumns.mockReturnValue([column]); + expect(convertToSiblingPipelineColumns({ agg, aggs: [], dataView })).toEqual(column); + expect(mockConvertToSchemaConfig).toBeCalledTimes(1); + expect(mockConvertMetricToColumns).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/std_deviation.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/std_deviation.test.ts new file mode 100644 index 000000000000..cbb1f03a6dc2 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/std_deviation.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertToStdDeviationFormulaColumns } from './std_deviation'; +import { FormulaColumn } from './types'; + +const mockGetFieldNameFromField = jest.fn(); +const mockGetFieldByName = jest.fn(); +const mockGetLabel = jest.fn(); + +jest.mock('../utils', () => ({ + getFieldNameFromField: jest.fn(() => mockGetFieldNameFromField()), + getLabel: jest.fn(() => mockGetLabel()), +})); + +describe('convertToStdDeviationFormulaColumns', () => { + const dataView = stubLogstashDataView; + const stdLowerAggId = 'agg-id.std_lower'; + const stdUpperAggId = 'agg-id.std_upper'; + const label = 'std label'; + const agg: SchemaConfig = { + accessor: 0, + label, + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.STD_DEV, + aggId: stdLowerAggId, + aggParams: { + field: dataView.fields[0].displayName, + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockGetFieldNameFromField.mockReturnValue(dataView.fields[0].displayName); + mockGetFieldByName.mockReturnValue(dataView.fields[0]); + mockGetLabel.mockReturnValue('some label'); + dataView.getFieldByName = mockGetFieldByName; + }); + + test.each< + [string, Parameters, Partial | null] + >([['null if no aggId is passed', [{ agg: { ...agg, aggId: undefined }, dataView }], null]])( + 'should return %s', + (_, input, expected) => { + if (expected === null) { + expect(convertToStdDeviationFormulaColumns(...input)).toBeNull(); + } else { + expect(convertToStdDeviationFormulaColumns(...input)).toEqual( + expect.objectContaining(expected) + ); + } + } + ); + + test('should return null if field is not present', () => { + mockGetFieldNameFromField.mockReturnValue(null); + expect(convertToStdDeviationFormulaColumns({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(0); + }); + + test("should return null if field doesn't exist in dataView", () => { + mockGetFieldByName.mockReturnValue(null); + dataView.getFieldByName = mockGetFieldByName; + expect(convertToStdDeviationFormulaColumns({ agg, dataView })).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return null if agg id is invalid', () => { + expect( + convertToStdDeviationFormulaColumns({ agg: { ...agg, aggId: 'some-id' }, dataView }) + ).toBeNull(); + expect(mockGetFieldNameFromField).toBeCalledTimes(1); + expect(dataView.getFieldByName).toBeCalledTimes(1); + }); + + test('should return formula column for lower std deviation', () => { + expect( + convertToStdDeviationFormulaColumns({ agg: { ...agg, aggId: stdLowerAggId }, dataView }) + ).toEqual( + expect.objectContaining({ + label, + meta: { aggId: 'agg-id.std_lower' }, + operationType: 'formula', + params: { formula: 'average(bytes) - 2 * standard_deviation(bytes)' }, + }) + ); + }); + + test('should return formula column for upper std deviation', () => { + expect( + convertToStdDeviationFormulaColumns({ agg: { ...agg, aggId: stdUpperAggId }, dataView }) + ).toEqual( + expect.objectContaining({ + label, + meta: { aggId: 'agg-id.std_upper' }, + operationType: 'formula', + params: { formula: 'average(bytes) + 2 * standard_deviation(bytes)' }, + }) + ); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/convert/terms.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/convert/terms.test.ts new file mode 100644 index 000000000000..d214ec74b09b --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/convert/terms.test.ts @@ -0,0 +1,241 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { AggParamsTerms, IAggConfig, METRIC_TYPES, BUCKET_TYPES } from '@kbn/data-plugin/common'; +import { convertToTermsColumn } from './terms'; +import { AggBasedColumn, TermsColumn } from './types'; +import { SchemaConfig } from '../../..'; + +const mockConvertMetricToColumns = jest.fn(); + +jest.mock('../metrics', () => ({ + convertMetricToColumns: jest.fn(() => mockConvertMetricToColumns()), +})); + +jest.mock('../../../vis_schemas', () => ({ + convertToSchemaConfig: jest.fn(() => ({})), +})); + +describe('convertToDateHistogramColumn', () => { + const aggId = `some-id`; + const aggParams: AggParamsTerms = { + field: stubLogstashDataView.fields[0].name, + orderBy: '_key', + order: { + value: 'asc', + text: '', + }, + size: 5, + }; + const aggs: Array> = [ + { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggParams: { + field: stubLogstashDataView.fields[0].name, + }, + }, + ]; + const metricColumns: AggBasedColumn[] = [ + { + columnId: 'column-1', + operationType: 'average', + isBucketed: false, + isSplit: false, + sourceField: stubLogstashDataView.fields[0].name, + dataType: 'number', + params: {}, + meta: { + aggId: '1', + }, + }, + ]; + + afterEach(() => { + jest.clearAllMocks(); + }); + + test.each< + [string, Parameters, Partial | null, () => void] + >([ + [ + 'null if dataview does not include field from terms params', + [ + aggId, + { + agg: { aggParams: { ...aggParams, field: '' } } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + null, + () => {}, + ], + [ + 'terms column with alphabetical orderBy', + [ + aggId, + { + agg: { aggParams } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + { + operationType: 'terms', + sourceField: stubLogstashDataView.fields[0].name, + isBucketed: true, + params: { + size: 5, + include: [], + exclude: [], + parentFormat: { id: 'terms' }, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + }, + }, + () => {}, + ], + [ + 'terms column with column orderBy if provided column for orderBy is exist', + [ + aggId, + { + agg: { aggParams: { ...aggParams, orderBy: '1' } } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + { + operationType: 'terms', + sourceField: stubLogstashDataView.fields[0].name, + isBucketed: true, + params: { + size: 5, + include: [], + exclude: [], + parentFormat: { id: 'terms' }, + orderBy: { type: 'column', columnId: metricColumns[0].columnId }, + orderAgg: metricColumns[0], + orderDirection: 'asc', + }, + }, + () => {}, + ], + [ + 'null if provided column for orderBy is not exist', + [ + aggId, + { + agg: { aggParams: { ...aggParams, orderBy: '2' } } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + null, + () => {}, + ], + [ + 'null if provided custom orderBy without orderAgg', + [ + aggId, + { + agg: { + aggParams: { ...aggParams, orderBy: 'custom', orderAgg: undefined }, + } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + null, + () => {}, + ], + [ + 'null if provided custom orderBy and not valid orderAgg', + [ + aggId, + { + agg: { + aggParams: { ...aggParams, orderBy: 'custom', orderAgg: {} as IAggConfig }, + } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + null, + () => { + mockConvertMetricToColumns.mockReturnValue(null); + }, + ], + [ + 'terms column with custom orderBy and prepared orderAgg', + [ + aggId, + { + agg: { + aggParams: { ...aggParams, orderBy: 'custom', orderAgg: {} as IAggConfig }, + } as SchemaConfig, + dataView: stubLogstashDataView, + aggs, + metricColumns, + }, + '', + false, + ], + { + operationType: 'terms', + sourceField: stubLogstashDataView.fields[0].name, + isBucketed: true, + params: { + size: 5, + include: [], + exclude: [], + parentFormat: { id: 'terms' }, + orderBy: { type: 'custom' }, + orderAgg: metricColumns[0], + orderDirection: 'asc', + }, + }, + () => { + mockConvertMetricToColumns.mockReturnValue(metricColumns); + }, + ], + ])('should return %s', (_, input, expected, actions) => { + actions(); + if (expected === null) { + expect(convertToTermsColumn(...input)).toBeNull(); + } else { + expect(convertToTermsColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/formula.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/formula.test.ts new file mode 100644 index 000000000000..95e128e22b09 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/formula.test.ts @@ -0,0 +1,474 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { DataViewField, IAggConfig, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { SchemaConfig } from '../../..'; +import { getFormulaForPipelineAgg, getFormulaForAgg } from './formula'; + +const mockGetMetricFromParentPipelineAgg = jest.fn(); +const mockIsPercentileAgg = jest.fn(); +const mockIsPercentileRankAgg = jest.fn(); +const mockIsPipeline = jest.fn(); +const mockIsStdDevAgg = jest.fn(); +const mockGetFieldByName = jest.fn(); +const originalGetFieldByName = stubLogstashDataView.getFieldByName; + +jest.mock('../utils', () => ({ + getFieldNameFromField: jest.fn((field) => field), + getMetricFromParentPipelineAgg: jest.fn(() => mockGetMetricFromParentPipelineAgg()), + isPercentileAgg: jest.fn(() => mockIsPercentileAgg()), + isPercentileRankAgg: jest.fn(() => mockIsPercentileRankAgg()), + isPipeline: jest.fn(() => mockIsPipeline()), + isStdDevAgg: jest.fn(() => mockIsStdDevAgg()), +})); + +const dataView = stubLogstashDataView; + +const field = stubLogstashDataView.fields[0].name; +const aggs: Array> = [ + { + aggId: '1', + aggType: METRIC_TYPES.CUMULATIVE_SUM, + aggParams: { customMetric: {} as IAggConfig }, + accessor: 0, + params: {}, + label: 'cumulative sum', + format: {}, + }, + { + aggId: '2', + aggType: METRIC_TYPES.AVG_BUCKET, + aggParams: { customMetric: {} as IAggConfig }, + accessor: 0, + params: {}, + label: 'overall average', + format: {}, + }, + { + aggId: '3.10', + aggType: METRIC_TYPES.PERCENTILES, + aggParams: { percents: [0, 10], field }, + accessor: 0, + params: {}, + label: 'percentile', + format: {}, + }, + { + aggId: '4.5', + aggType: METRIC_TYPES.PERCENTILE_RANKS, + aggParams: { values: [0, 5], field }, + accessor: 0, + params: {}, + label: 'percintile rank', + format: {}, + }, + { + aggId: '5.std_upper', + aggType: METRIC_TYPES.STD_DEV, + aggParams: { field }, + accessor: 0, + params: {}, + label: 'std dev', + format: {}, + }, + { + aggId: '6', + aggType: METRIC_TYPES.AVG, + aggParams: { field }, + accessor: 0, + params: {}, + label: 'average', + format: {}, + }, +]; + +describe('getFormulaForPipelineAgg', () => { + afterEach(() => { + jest.clearAllMocks(); + dataView.getFieldByName = originalGetFieldByName; + }); + + test.each<[string, Parameters, () => void, string | null]>([ + [ + 'null if custom metric is invalid', + [{ agg: aggs[0] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue(null); + }, + null, + ], + [ + 'null if custom metric type is not supported', + [{ agg: aggs[0] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValue({ + aggType: METRIC_TYPES.GEO_BOUNDS, + }); + }, + null, + ], + [ + 'correct formula if agg is parent pipeline agg and custom metric is valid and supported pipeline agg', + [{ agg: aggs[0] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg + .mockReturnValueOnce({ + aggType: METRIC_TYPES.MOVING_FN, + aggParams: {}, + aggId: '2', + }) + .mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '3', + }); + }, + 'cumulative_sum(moving_average(average(bytes)))', + ], + [ + 'correct formula if agg is parent pipeline agg and custom metric is valid and supported not pipeline agg', + [{ agg: aggs[0] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '2', + }); + }, + 'cumulative_sum(average(bytes))', + ], + [ + 'correct formula if agg is parent pipeline agg and custom metric is valid and supported percentile rank agg', + [{ agg: aggs[0] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.PERCENTILE_RANKS, + aggParams: { + field, + }, + aggId: '3.10', + }); + }, + 'cumulative_sum(percentile_rank(bytes, value=10))', + ], + [ + 'correct formula if agg is sibling pipeline agg and custom metric is valid and supported agg', + [{ agg: aggs[1] as SchemaConfig, aggs, dataView }], + () => { + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '3', + }); + }, + 'average(bytes)', + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(getFormulaForPipelineAgg(...input)).toBeNull(); + } else { + expect(getFormulaForPipelineAgg(...input)).toEqual(expected); + } + }); + + test('null if agg is sibling pipeline agg, custom metric is valid, agg is supported and field type is not supported', () => { + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '3', + }); + + const field1: DataViewField = { + name: 'bytes', + type: 'geo', + esTypes: ['long'], + aggregatable: true, + searchable: true, + count: 10, + readFromDocValues: true, + scripted: false, + isMapped: true, + } as DataViewField; + + mockGetFieldByName.mockReturnValueOnce(field1); + + dataView.getFieldByName = mockGetFieldByName; + const agg = getFormulaForPipelineAgg({ + agg: aggs[1] as SchemaConfig, + aggs, + dataView, + }); + expect(agg).toBeNull(); + }); + + test('null if agg is sibling pipeline agg, custom metric is valid, agg is supported, field type is supported and field is not aggregatable', () => { + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '3', + }); + + const field1: DataViewField = { + name: 'str', + type: 'string', + esTypes: ['text'], + aggregatable: false, + searchable: true, + count: 10, + readFromDocValues: true, + scripted: false, + isMapped: true, + } as DataViewField; + + mockGetFieldByName.mockReturnValueOnce(field1); + + dataView.getFieldByName = mockGetFieldByName; + const agg = getFormulaForPipelineAgg({ + agg: aggs[1] as SchemaConfig, + aggs, + dataView, + }); + expect(agg).toBeNull(); + }); +}); + +describe('getFormulaForAgg', () => { + beforeEach(() => { + mockIsPercentileAgg.mockReturnValue(false); + mockIsPipeline.mockReturnValue(false); + mockIsStdDevAgg.mockReturnValue(false); + mockIsPercentileRankAgg.mockReturnValue(false); + }); + + afterEach(() => { + jest.clearAllMocks(); + dataView.getFieldByName = originalGetFieldByName; + }); + + test.each<[string, Parameters, () => void, string | null]>([ + [ + 'null if agg type is not supported', + [ + { + agg: { ...aggs[0], aggType: METRIC_TYPES.GEO_BOUNDS, aggParams: { field } }, + aggs, + dataView, + }, + ], + () => {}, + null, + ], + [ + 'correct pipeline formula if agg is valid pipeline agg', + [{ agg: aggs[0], aggs, dataView }], + () => { + mockIsPipeline.mockReturnValue(true); + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '2', + }); + }, + 'cumulative_sum(average(bytes))', + ], + [ + 'correct percentile formula if agg is valid percentile agg', + [{ agg: aggs[2], aggs, dataView }], + () => { + mockIsPercentileAgg.mockReturnValue(true); + }, + 'percentile(bytes, percentile=10)', + ], + [ + 'correct percentile rank formula if agg is valid percentile rank agg', + [{ agg: aggs[3], aggs, dataView }], + () => { + mockIsPercentileRankAgg.mockReturnValue(true); + }, + 'percentile_rank(bytes, value=5)', + ], + [ + 'correct standart deviation formula if agg is valid standart deviation agg', + [{ agg: aggs[4], aggs, dataView }], + () => { + mockIsStdDevAgg.mockReturnValue(true); + }, + 'average(bytes) + 2 * standard_deviation(bytes)', + ], + [ + 'correct metric formula if agg is valid other metric agg', + [{ agg: aggs[5], aggs, dataView }], + () => {}, + 'average(bytes)', + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(getFormulaForAgg(...input)).toBeNull(); + } else { + expect(getFormulaForAgg(...input)).toEqual(expected); + } + }); + + test.each([ + [ + 'null if agg is valid pipeline agg', + aggs[0], + () => { + mockIsPipeline.mockReturnValue(true); + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '2', + }); + }, + ], + [ + 'null if percentile rank agg is valid percentile agg', + aggs[2], + () => { + mockIsPercentileAgg.mockReturnValue(true); + }, + ], + [ + 'null if agg is valid percentile rank agg', + aggs[3], + () => { + mockIsPercentileRankAgg.mockReturnValue(true); + }, + ], + [ + 'null if agg is valid standart deviation agg', + aggs[4], + () => { + mockIsStdDevAgg.mockReturnValue(true); + }, + ], + ['null if agg is valid other metric agg', aggs[5], () => {}], + ])('should return %s and field type is not supported', (_, agg, actions) => { + actions(); + const field1: DataViewField = { + name: 'bytes', + type: 'geo', + esTypes: ['long'], + aggregatable: true, + searchable: true, + count: 10, + readFromDocValues: true, + scripted: false, + isMapped: true, + } as DataViewField; + + mockGetFieldByName.mockReturnValueOnce(field1); + + dataView.getFieldByName = mockGetFieldByName; + const result = getFormulaForPipelineAgg({ + agg: agg as SchemaConfig< + | METRIC_TYPES.CUMULATIVE_SUM + | METRIC_TYPES.DERIVATIVE + | METRIC_TYPES.MOVING_FN + | METRIC_TYPES.AVG_BUCKET + | METRIC_TYPES.MAX_BUCKET + | METRIC_TYPES.MIN_BUCKET + | METRIC_TYPES.SUM_BUCKET + >, + aggs, + dataView, + }); + expect(result).toBeNull(); + }); + + test.each([ + [ + 'null if agg is valid pipeline agg', + aggs[0], + () => { + mockIsPipeline.mockReturnValue(true); + mockGetMetricFromParentPipelineAgg.mockReturnValueOnce({ + aggType: METRIC_TYPES.AVG, + aggParams: { + field, + }, + aggId: '2', + }); + }, + ], + [ + 'null if percentile rank agg is valid percentile agg', + aggs[2], + () => { + mockIsPercentileAgg.mockReturnValue(true); + }, + ], + [ + 'null if agg is valid percentile rank agg', + aggs[3], + () => { + mockIsPercentileRankAgg.mockReturnValue(true); + }, + ], + [ + 'null if agg is valid standart deviation agg', + aggs[4], + () => { + mockIsStdDevAgg.mockReturnValue(true); + }, + ], + ['null if agg is valid other metric agg', aggs[5], () => {}], + ])( + 'should return %s, field type is supported and field is not aggregatable', + (_, agg, actions) => { + actions(); + const field1: DataViewField = { + name: 'str', + type: 'string', + esTypes: ['text'], + aggregatable: false, + searchable: true, + count: 10, + readFromDocValues: true, + scripted: false, + isMapped: true, + } as DataViewField; + + mockGetFieldByName.mockReturnValueOnce(field1); + + dataView.getFieldByName = mockGetFieldByName; + const result = getFormulaForPipelineAgg({ + agg: agg as SchemaConfig< + | METRIC_TYPES.CUMULATIVE_SUM + | METRIC_TYPES.DERIVATIVE + | METRIC_TYPES.MOVING_FN + | METRIC_TYPES.AVG_BUCKET + | METRIC_TYPES.MAX_BUCKET + | METRIC_TYPES.MIN_BUCKET + | METRIC_TYPES.SUM_BUCKET + >, + aggs, + dataView, + }); + expect(result).toBeNull(); + } + ); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts new file mode 100644 index 000000000000..659ab80ea03d --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/metrics.test.ts @@ -0,0 +1,368 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { SchemaConfig } from '../../..'; +import { convertMetricToColumns } from './metrics'; + +const mockConvertMetricAggregationColumnWithoutSpecialParams = jest.fn(); +const mockConvertToOtherParentPipelineAggColumns = jest.fn(); +const mockConvertToPercentileColumn = jest.fn(); +const mockConvertToPercentileRankColumn = jest.fn(); +const mockConvertToSiblingPipelineColumns = jest.fn(); +const mockConvertToStdDeviationFormulaColumns = jest.fn(); +const mockConvertToLastValueColumn = jest.fn(); +const mockConvertToCumulativeSumAggColumn = jest.fn(); + +jest.mock('../convert', () => ({ + convertMetricAggregationColumnWithoutSpecialParams: jest.fn(() => + mockConvertMetricAggregationColumnWithoutSpecialParams() + ), + convertToOtherParentPipelineAggColumns: jest.fn(() => + mockConvertToOtherParentPipelineAggColumns() + ), + convertToPercentileColumn: jest.fn(() => mockConvertToPercentileColumn()), + convertToPercentileRankColumn: jest.fn(() => mockConvertToPercentileRankColumn()), + convertToSiblingPipelineColumns: jest.fn(() => mockConvertToSiblingPipelineColumns()), + convertToStdDeviationFormulaColumns: jest.fn(() => mockConvertToStdDeviationFormulaColumns()), + convertToLastValueColumn: jest.fn(() => mockConvertToLastValueColumn()), + convertToCumulativeSumAggColumn: jest.fn(() => mockConvertToCumulativeSumAggColumn()), +})); + +describe('convertMetricToColumns invalid cases', () => { + const dataView = stubLogstashDataView; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + beforeAll(() => { + mockConvertMetricAggregationColumnWithoutSpecialParams.mockReturnValue(null); + mockConvertToOtherParentPipelineAggColumns.mockReturnValue(null); + mockConvertToPercentileColumn.mockReturnValue(null); + mockConvertToPercentileRankColumn.mockReturnValue(null); + mockConvertToSiblingPipelineColumns.mockReturnValue(null); + mockConvertToStdDeviationFormulaColumns.mockReturnValue(null); + mockConvertToLastValueColumn.mockReturnValue(null); + mockConvertToCumulativeSumAggColumn.mockReturnValue(null); + }); + + test.each<[string, Parameters, null, jest.Mock | undefined]>([ + [ + 'null if agg is not supported', + [{ aggType: METRIC_TYPES.GEO_BOUNDS } as unknown as SchemaConfig, dataView, []], + null, + undefined, + ], + [ + 'null if supported agg AVG is not valid', + [{ aggType: METRIC_TYPES.AVG } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg MIN is not valid', + [{ aggType: METRIC_TYPES.MIN } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg MAX is not valid', + [{ aggType: METRIC_TYPES.MAX } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg SUM is not valid', + [{ aggType: METRIC_TYPES.SUM } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg COUNT is not valid', + [{ aggType: METRIC_TYPES.COUNT } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg CARDINALITY is not valid', + [{ aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg VALUE_COUNT is not valid', + [{ aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg MEDIAN is not valid', + [{ aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, dataView, []], + null, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'null if supported agg STD_DEV is not valid', + [{ aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, dataView, []], + null, + mockConvertToStdDeviationFormulaColumns, + ], + [ + 'null if supported agg PERCENTILES is not valid', + [{ aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, dataView, []], + null, + mockConvertToPercentileColumn, + ], + [ + 'null if supported agg SINGLE_PERCENTILE is not valid', + [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, dataView, []], + null, + mockConvertToPercentileColumn, + ], + [ + 'null if supported agg PERCENTILE_RANKS is not valid', + [{ aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, dataView, []], + null, + mockConvertToPercentileRankColumn, + ], + [ + 'null if supported agg SINGLE_PERCENTILE_RANK is not valid', + [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, dataView, []], + null, + mockConvertToPercentileRankColumn, + ], + [ + 'null if supported agg TOP_HITS is not valid', + [{ aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, dataView, []], + null, + mockConvertToLastValueColumn, + ], + [ + 'null if supported agg TOP_METRICS is not valid', + [{ aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, dataView, []], + null, + mockConvertToLastValueColumn, + ], + [ + 'null if supported agg CUMULATIVE_SUM is not valid', + [{ aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, dataView, []], + null, + mockConvertToCumulativeSumAggColumn, + ], + [ + 'null if supported agg DERIVATIVE is not valid', + [{ aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, dataView, []], + null, + mockConvertToOtherParentPipelineAggColumns, + ], + [ + 'null if supported agg MOVING_FN is not valid', + [{ aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, dataView, []], + null, + mockConvertToOtherParentPipelineAggColumns, + ], + [ + 'null if supported agg SUM_BUCKET is not valid', + [{ aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, dataView, []], + null, + mockConvertToSiblingPipelineColumns, + ], + [ + 'null if supported agg MIN_BUCKET is not valid', + [{ aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, dataView, []], + null, + mockConvertToSiblingPipelineColumns, + ], + [ + 'null if supported agg MAX_BUCKET is not valid', + [{ aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, dataView, []], + null, + mockConvertToSiblingPipelineColumns, + ], + [ + 'null if supported agg AVG_BUCKET is not valid', + [{ aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, dataView, []], + null, + mockConvertToSiblingPipelineColumns, + ], + [ + 'null if supported agg SERIAL_DIFF is not valid', + [{ aggType: METRIC_TYPES.SERIAL_DIFF } as SchemaConfig, dataView, []], + null, + undefined, + ], + ])('should return %s', (_, input, expected, mock) => { + expect(convertMetricToColumns(...input)).toBeNull(); + + if (mock) { + expect(mock).toBeCalledTimes(1); + } + }); +}); +describe('convertMetricToColumns valid cases', () => { + const dataView = stubLogstashDataView; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const result = [{}]; + + beforeAll(() => { + mockConvertMetricAggregationColumnWithoutSpecialParams.mockReturnValue(result); + mockConvertToOtherParentPipelineAggColumns.mockReturnValue(result); + mockConvertToPercentileColumn.mockReturnValue(result); + mockConvertToPercentileRankColumn.mockReturnValue(result); + mockConvertToSiblingPipelineColumns.mockReturnValue(result); + mockConvertToStdDeviationFormulaColumns.mockReturnValue(result); + mockConvertToLastValueColumn.mockReturnValue(result); + mockConvertToCumulativeSumAggColumn.mockReturnValue(result); + }); + + test.each<[string, Parameters, Array<{}>, jest.Mock]>([ + [ + 'array of columns if supported agg AVG is valid', + [{ aggType: METRIC_TYPES.AVG } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg MIN is valid', + [{ aggType: METRIC_TYPES.MIN } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg MAX is valid', + [{ aggType: METRIC_TYPES.MAX } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg SUM is valid', + [{ aggType: METRIC_TYPES.SUM } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg COUNT is valid', + [{ aggType: METRIC_TYPES.COUNT } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg CARDINALITY is valid', + [{ aggType: METRIC_TYPES.CARDINALITY } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg VALUE_COUNT is valid', + [{ aggType: METRIC_TYPES.VALUE_COUNT } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg MEDIAN is valid', + [{ aggType: METRIC_TYPES.MEDIAN } as SchemaConfig, dataView, []], + result, + mockConvertMetricAggregationColumnWithoutSpecialParams, + ], + [ + 'array of columns if supported agg STD_DEV is valid', + [{ aggType: METRIC_TYPES.STD_DEV } as SchemaConfig, dataView, []], + result, + mockConvertToStdDeviationFormulaColumns, + ], + [ + 'array of columns if supported agg PERCENTILES is valid', + [{ aggType: METRIC_TYPES.PERCENTILES } as SchemaConfig, dataView, []], + result, + mockConvertToPercentileColumn, + ], + [ + 'array of columns if supported agg SINGLE_PERCENTILE is valid', + [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE } as SchemaConfig, dataView, []], + result, + mockConvertToPercentileColumn, + ], + [ + 'array of columns if supported agg PERCENTILE_RANKS is valid', + [{ aggType: METRIC_TYPES.PERCENTILE_RANKS } as SchemaConfig, dataView, []], + result, + mockConvertToPercentileRankColumn, + ], + [ + 'array of columns if supported agg SINGLE_PERCENTILE_RANK is valid', + [{ aggType: METRIC_TYPES.SINGLE_PERCENTILE_RANK } as SchemaConfig, dataView, []], + result, + mockConvertToPercentileRankColumn, + ], + [ + 'array of columns if supported agg TOP_HITS is valid', + [{ aggType: METRIC_TYPES.TOP_HITS } as SchemaConfig, dataView, []], + result, + mockConvertToLastValueColumn, + ], + [ + 'array of columns if supported agg TOP_METRICS is valid', + [{ aggType: METRIC_TYPES.TOP_METRICS } as SchemaConfig, dataView, []], + result, + mockConvertToLastValueColumn, + ], + [ + 'array of columns if supported agg CUMULATIVE_SUM is valid', + [{ aggType: METRIC_TYPES.CUMULATIVE_SUM } as SchemaConfig, dataView, []], + result, + mockConvertToCumulativeSumAggColumn, + ], + [ + 'array of columns if supported agg DERIVATIVE is valid', + [{ aggType: METRIC_TYPES.DERIVATIVE } as SchemaConfig, dataView, []], + result, + mockConvertToOtherParentPipelineAggColumns, + ], + [ + 'array of columns if supported agg MOVING_FN is valid', + [{ aggType: METRIC_TYPES.MOVING_FN } as SchemaConfig, dataView, []], + result, + mockConvertToOtherParentPipelineAggColumns, + ], + [ + 'array of columns if supported agg SUM_BUCKET is valid', + [{ aggType: METRIC_TYPES.SUM_BUCKET } as SchemaConfig, dataView, []], + result, + mockConvertToSiblingPipelineColumns, + ], + [ + 'array of columns if supported agg MIN_BUCKET is valid', + [{ aggType: METRIC_TYPES.MIN_BUCKET } as SchemaConfig, dataView, []], + result, + mockConvertToSiblingPipelineColumns, + ], + [ + 'array of columns if supported agg MAX_BUCKET is valid', + [{ aggType: METRIC_TYPES.MAX_BUCKET } as SchemaConfig, dataView, []], + result, + mockConvertToSiblingPipelineColumns, + ], + [ + 'array of columns if supported agg AVG_BUCKET is valid', + [{ aggType: METRIC_TYPES.AVG_BUCKET } as SchemaConfig, dataView, []], + result, + mockConvertToSiblingPipelineColumns, + ], + ])('should return %s', (_, input, expected, mock) => { + expect(convertMetricToColumns(...input)).toEqual(expected.map(expect.objectContaining)); + if (mock) { + expect(mock).toBeCalledTimes(1); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/metrics/percentage_formula.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/percentage_formula.test.ts new file mode 100644 index 000000000000..9855ce44b660 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/metrics/percentage_formula.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { METRIC_TYPES } from '@kbn/data-plugin/common'; +import { getPercentageColumnFormulaColumn } from './percentage_formula'; +import { FormulaColumn } from '../../types'; +import { SchemaConfig } from '../../..'; + +const mockGetFormulaForAgg = jest.fn(); +const mockCreateFormulaColumn = jest.fn(); + +jest.mock('./formula', () => ({ + getFormulaForAgg: jest.fn(() => mockGetFormulaForAgg()), +})); + +jest.mock('../convert', () => ({ + createFormulaColumn: jest.fn((formula) => mockCreateFormulaColumn(formula)), +})); + +describe('getPercentageColumnFormulaColumn', () => { + const dataView = stubLogstashDataView; + const field = stubLogstashDataView.fields[0].name; + const aggs: Array> = [ + { + aggId: '1', + aggType: METRIC_TYPES.AVG, + aggParams: { field }, + accessor: 0, + params: {}, + label: 'average', + format: {}, + }, + ]; + + afterEach(() => { + jest.clearAllMocks(); + }); + + test.each< + [ + string, + Parameters, + () => void, + Partial | null + ] + >([ + [ + 'null if cannot build formula for provided agg', + [{ agg: aggs[0], aggs, dataView }], + () => { + mockGetFormulaForAgg.mockReturnValue(null); + }, + null, + ], + [ + 'null if cannot create formula column for provided arguments', + [{ agg: aggs[0], aggs, dataView }], + () => { + mockGetFormulaForAgg.mockReturnValue('test-formula'); + mockCreateFormulaColumn.mockReturnValue(null); + }, + null, + ], + [ + 'formula column if provided arguments are valid', + [{ agg: aggs[0], aggs, dataView }], + () => { + mockGetFormulaForAgg.mockReturnValue('test-formula'); + mockCreateFormulaColumn.mockImplementation((formula) => ({ + operationType: 'formula', + params: { formula }, + label: 'Average', + })); + }, + { + operationType: 'formula', + params: { + formula: `(test-formula) / overall_sum(test-formula)`, + format: { id: 'percent' }, + }, + label: `Average percentages`, + }, + ], + ])('should return %s', (_, input, actions, expected) => { + actions(); + if (expected === null) { + expect(getPercentageColumnFormulaColumn(...input)).toBeNull(); + } else { + expect(getPercentageColumnFormulaColumn(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/utils.test.ts b/src/plugins/visualizations/common/convert_to_lens/lib/utils.test.ts new file mode 100644 index 000000000000..73118b6ad4f0 --- /dev/null +++ b/src/plugins/visualizations/common/convert_to_lens/lib/utils.test.ts @@ -0,0 +1,521 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { IAggConfig, METRIC_TYPES } from '@kbn/data-plugin/common'; +import { AggBasedColumn, ColumnWithMeta, Operations } from '../..'; +import { SchemaConfig } from '../../types'; +import { + getCustomBucketsFromSiblingAggs, + getFieldNameFromField, + getLabel, + getLabelForPercentile, + getMetricFromParentPipelineAgg, + getValidColumns, + isColumnWithMeta, + isMetricAggWithoutParams, + isPercentileAgg, + isPercentileRankAgg, + isPipeline, + isSchemaConfig, + isSiblingPipeline, + isStdDevAgg, +} from './utils'; + +describe('getLabel', () => { + const label = 'some label'; + const customLabel = 'some custom label'; + + const agg: SchemaConfig = { + accessor: 0, + label, + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggId: 'id', + aggParams: { field: 'some-field' }, + }; + + test('should return label', () => { + const { aggParams, ...aggWithoutAggParams } = agg; + expect(getLabel(aggWithoutAggParams)).toEqual(label); + expect(getLabel(agg)).toEqual(label); + expect(getLabel({ ...agg, aggParams: { ...aggParams!, customLabel: undefined } })).toEqual( + label + ); + }); + + test('should return customLabel', () => { + const aggParams = { ...agg.aggParams!, customLabel }; + const aggWithCustomLabel = { ...agg, aggParams }; + expect(getLabel(aggWithCustomLabel)).toEqual(customLabel); + }); +}); + +describe('getLabelForPercentile', () => { + const label = 'some label'; + const customLabel = 'some custom label'; + + const agg: SchemaConfig = { + accessor: 0, + label, + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.PERCENTILES, + aggId: 'id', + aggParams: { field: 'some-field' }, + }; + + test('should return empty string if no custom label is specified', () => { + const { aggParams, ...aggWithoutAggParams } = agg; + expect(getLabelForPercentile(aggWithoutAggParams)).toEqual(''); + expect(getLabel({ ...agg, aggParams: { ...aggParams!, customLabel: '' } })).toEqual(''); + }); + + test('should return label if custom label is specified', () => { + const aggParams = { ...agg.aggParams!, customLabel }; + const aggWithCustomLabel = { ...agg, aggParams }; + expect(getLabelForPercentile(aggWithCustomLabel)).toEqual(label); + }); +}); + +describe('getValidColumns', () => { + const dataView = stubLogstashDataView; + const columns: AggBasedColumn[] = [ + { + operationType: Operations.AVERAGE, + sourceField: dataView.fields[0].name, + columnId: 'some-id-0', + dataType: 'number', + params: {}, + meta: { aggId: 'aggId-0' }, + isSplit: false, + isBucketed: true, + }, + { + operationType: Operations.SUM, + sourceField: dataView.fields[0].name, + columnId: 'some-id-1', + dataType: 'number', + params: {}, + meta: { aggId: 'aggId-1' }, + isSplit: false, + isBucketed: true, + }, + ]; + test.each<[string, Parameters, AggBasedColumn[] | null]>([ + ['null if array contains null', [[null, ...columns]], null], + ['null if columns is null', [null], null], + ['null if columns is undefined', [undefined], null], + ['columns', [columns], columns], + ['columns if one column is passed', [columns[0]], [columns[0]]], + ])('should return %s', (_, input, expected) => { + if (expected === null) { + expect(getValidColumns(...input)).toBeNull(); + } else { + expect(getValidColumns(...input)).toEqual(expect.objectContaining(expected)); + } + }); +}); + +describe('getFieldNameFromField', () => { + test('should return null if no field is passed', () => { + expect(getFieldNameFromField(undefined)).toBeNull(); + }); + + test('should return field name if field is string', () => { + const fieldName = 'some-field-name'; + expect(getFieldNameFromField(fieldName)).toEqual(fieldName); + }); + + test('should return field name if field is DataViewField', () => { + const field = stubLogstashDataView.fields[0]; + expect(getFieldNameFromField(field)).toEqual(field.name); + }); +}); + +describe('isSchemaConfig', () => { + const iAggConfig = { + id: '', + enabled: false, + params: {}, + } as IAggConfig; + + const schemaConfig: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + }; + + test('should be false if is IAggConfig', () => { + expect(isSchemaConfig(iAggConfig)).toBeFalsy(); + }); + + test('should be false if is SchemaConfig', () => { + expect(isSchemaConfig(schemaConfig)).toBeTruthy(); + }); +}); + +describe('isColumnWithMeta', () => { + const column: AggBasedColumn = { + sourceField: '', + columnId: '', + operationType: 'terms', + isBucketed: false, + isSplit: false, + dataType: 'string', + } as AggBasedColumn; + + const columnWithMeta: ColumnWithMeta = { + sourceField: '', + columnId: '', + operationType: 'average', + isBucketed: false, + isSplit: false, + dataType: 'string', + params: {}, + meta: { aggId: 'some-agg-id' }, + }; + + test('should return false if column without meta', () => { + expect(isColumnWithMeta(column)).toBeFalsy(); + }); + + test('should return true if column with meta', () => { + expect(isColumnWithMeta(columnWithMeta)).toBeTruthy(); + }); +}); + +describe('isSiblingPipeline', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.AVG_BUCKET, true], + [METRIC_TYPES.SUM_BUCKET, true], + [METRIC_TYPES.MAX_BUCKET, true], + [METRIC_TYPES.MIN_BUCKET, true], + [METRIC_TYPES.CUMULATIVE_SUM, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isSiblingPipeline({ ...metric, aggType } as SchemaConfig)).toBe( + expected + ); + }); +}); + +describe('isPipeline', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.AVG_BUCKET, true], + [METRIC_TYPES.SUM_BUCKET, true], + [METRIC_TYPES.MAX_BUCKET, true], + [METRIC_TYPES.MIN_BUCKET, true], + [METRIC_TYPES.CUMULATIVE_SUM, true], + [METRIC_TYPES.DERIVATIVE, true], + [METRIC_TYPES.MOVING_FN, true], + [METRIC_TYPES.AVG, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isPipeline({ ...metric, aggType } as SchemaConfig)).toBe(expected); + }); +}); + +describe('isMetricAggWithoutParams', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.AVG, true], + [METRIC_TYPES.COUNT, true], + [METRIC_TYPES.MAX, true], + [METRIC_TYPES.MIN, true], + [METRIC_TYPES.SUM, true], + [METRIC_TYPES.MEDIAN, true], + [METRIC_TYPES.CARDINALITY, true], + [METRIC_TYPES.VALUE_COUNT, true], + [METRIC_TYPES.DERIVATIVE, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isMetricAggWithoutParams({ ...metric, aggType } as SchemaConfig)).toBe( + expected + ); + }); +}); + +describe('isPercentileAgg', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.PERCENTILES, true], + [METRIC_TYPES.DERIVATIVE, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isPercentileAgg({ ...metric, aggType } as SchemaConfig)).toBe(expected); + }); +}); + +describe('isPercentileRankAgg', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.PERCENTILE_RANKS, true], + [METRIC_TYPES.PERCENTILES, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isPercentileRankAgg({ ...metric, aggType } as SchemaConfig)).toBe( + expected + ); + }); +}); + +describe('isStdDevAgg', () => { + const metric: Omit = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + }; + + test.each<[METRIC_TYPES, boolean]>([ + [METRIC_TYPES.STD_DEV, true], + [METRIC_TYPES.PERCENTILES, false], + ])('for %s should return %s', (aggType, expected) => { + expect(isStdDevAgg({ ...metric, aggType } as SchemaConfig)).toBe(expected); + }); +}); + +describe('getCustomBucketsFromSiblingAggs', () => { + const bucket1 = { + id: 'some-id', + params: { type: 'some-type' }, + type: 'type1', + enabled: true, + } as unknown as IAggConfig; + const serialize1 = () => bucket1; + + const bucket2 = { + id: 'some-id-1', + params: { type: 'some-type-1' }, + type: 'type2', + enabled: false, + } as unknown as IAggConfig; + const serialize2 = () => bucket2; + + const bucketWithSerialize1 = { ...bucket1, serialize: serialize1 } as unknown as IAggConfig; + const metric1: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG_BUCKET, + aggId: 'some-agg-id', + aggParams: { + customBucket: bucketWithSerialize1, + }, + }; + + const bucketWithSerialize2 = { ...bucket2, serialize: serialize2 } as unknown as IAggConfig; + const metric2: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG_BUCKET, + aggId: 'some-agg-id', + aggParams: { + customBucket: bucketWithSerialize2, + }, + }; + const bucket3 = { ...bucket1, id: 'other id' } as unknown as IAggConfig; + const serialize3 = () => bucket3; + + const bucketWithSerialize3 = { ...bucket3, serialize: serialize3 } as unknown as IAggConfig; + const metric3: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG_BUCKET, + aggId: 'some-agg-id', + aggParams: { + customBucket: bucketWithSerialize3, + }, + }; + + test("should filter out duplicated custom buckets, ignoring id's", () => { + expect(getCustomBucketsFromSiblingAggs([metric1, metric2, metric3])).toEqual([ + bucketWithSerialize1, + bucketWithSerialize2, + ]); + }); +}); + +const mockConvertToSchemaConfig = jest.fn(); + +jest.mock('../../vis_schemas', () => ({ + convertToSchemaConfig: jest.fn(() => mockConvertToSchemaConfig()), +})); + +describe('getMetricFromParentPipelineAgg', () => { + const metricAggId = 'agg-id-0'; + const aggId = 'agg-id-1'; + const plainAgg: SchemaConfig = { + accessor: 0, + label: 'some-label', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG, + aggId: metricAggId, + }; + const agg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.AVG_BUCKET, + aggParams: { customMetric: {} as IAggConfig }, + aggId, + }; + + const parentPipelineAgg: SchemaConfig = { + accessor: 0, + label: '', + format: { + id: undefined, + params: undefined, + }, + params: {}, + aggType: METRIC_TYPES.CUMULATIVE_SUM, + aggParams: { metricAgg: 'custom' }, + aggId, + }; + + const metric = { aggType: METRIC_TYPES.CUMULATIVE_SUM }; + beforeEach(() => { + jest.clearAllMocks(); + }); + + beforeAll(() => { + mockConvertToSchemaConfig.mockReturnValue(metric); + }); + + test('should return null if aggParams are undefined', () => { + expect(getMetricFromParentPipelineAgg({ ...agg, aggParams: undefined }, [])).toBeNull(); + expect(mockConvertToSchemaConfig).toBeCalledTimes(0); + }); + + test('should return null if is sibling pipeline agg and custom metric is not defined', () => { + expect( + getMetricFromParentPipelineAgg({ ...agg, aggParams: { customMetric: undefined } }, []) + ).toBeNull(); + expect(mockConvertToSchemaConfig).toBeCalledTimes(0); + }); + + test('should return null if is parent pipeline agg, metricAgg is custom and custom metric is not defined', () => { + expect(getMetricFromParentPipelineAgg(parentPipelineAgg, [])).toBeNull(); + expect(mockConvertToSchemaConfig).toBeCalledTimes(0); + }); + + test('should return metric if is parent pipeline agg, metricAgg is equal to aggId and custom metric is not defined', () => { + const parentPipelineAggWithLink = { + ...parentPipelineAgg, + aggParams: { + metricAgg: metricAggId, + }, + }; + expect( + getMetricFromParentPipelineAgg(parentPipelineAggWithLink, [ + parentPipelineAggWithLink, + plainAgg, + ]) + ).toEqual(plainAgg); + expect(mockConvertToSchemaConfig).toBeCalledTimes(0); + }); + + test('should return metric if sibling pipeline agg with custom metric', () => { + expect(getMetricFromParentPipelineAgg(agg, [agg])).toEqual(metric); + expect(mockConvertToSchemaConfig).toBeCalledTimes(1); + }); + + test('should return metric if parent pipeline agg with custom metric', () => { + expect( + getMetricFromParentPipelineAgg( + { + ...parentPipelineAgg, + aggParams: { ...parentPipelineAgg.aggParams, customMetric: {} as IAggConfig }, + }, + [agg] + ) + ).toEqual(metric); + expect(mockConvertToSchemaConfig).toBeCalledTimes(1); + }); +}); diff --git a/src/plugins/visualizations/common/convert_to_lens/lib/utils.ts b/src/plugins/visualizations/common/convert_to_lens/lib/utils.ts index 39920e525b79..c4e5c5474bf0 100644 --- a/src/plugins/visualizations/common/convert_to_lens/lib/utils.ts +++ b/src/plugins/visualizations/common/convert_to_lens/lib/utils.ts @@ -150,7 +150,7 @@ export const isStdDevAgg = (metric: SchemaConfig): metric is SchemaConfig { +export const getCustomBucketsFromSiblingAggs = (metrics: SchemaConfig[]) => { return metrics.reduce((acc, metric) => { if ( isSiblingPipeline(metric) && diff --git a/src/plugins/visualizations/public/convert_to_lens/schemas.test.ts b/src/plugins/visualizations/public/convert_to_lens/schemas.test.ts index dccd579d95db..5b8b7832730b 100644 --- a/src/plugins/visualizations/public/convert_to_lens/schemas.test.ts +++ b/src/plugins/visualizations/public/convert_to_lens/schemas.test.ts @@ -40,7 +40,7 @@ jest.mock('../../common/convert_to_lens/lib/buckets', () => ({ })); jest.mock('../../common/convert_to_lens/lib/utils', () => ({ - getCutomBucketsFromSiblingAggs: jest.fn(() => mockGetCutomBucketsFromSiblingAggs()), + getCustomBucketsFromSiblingAggs: jest.fn(() => mockGetCutomBucketsFromSiblingAggs()), })); jest.mock('../vis_schemas', () => ({ diff --git a/src/plugins/visualizations/public/convert_to_lens/schemas.ts b/src/plugins/visualizations/public/convert_to_lens/schemas.ts index e9b467074b6f..56108b1a1d63 100644 --- a/src/plugins/visualizations/public/convert_to_lens/schemas.ts +++ b/src/plugins/visualizations/public/convert_to_lens/schemas.ts @@ -11,7 +11,7 @@ import { METRIC_TYPES, TimefilterContract } from '@kbn/data-plugin/public'; import { AggBasedColumn, SchemaConfig } from '../../common'; import { convertMetricToColumns } from '../../common/convert_to_lens/lib/metrics'; import { convertBucketToColumns } from '../../common/convert_to_lens/lib/buckets'; -import { getCutomBucketsFromSiblingAggs } from '../../common/convert_to_lens/lib/utils'; +import { getCustomBucketsFromSiblingAggs } from '../../common/convert_to_lens/lib/utils'; import type { Vis } from '../types'; import { getVisSchemas, Schemas } from '../vis_schemas'; import { @@ -57,7 +57,7 @@ export const getColumnsFromVis = ( return null; } - const customBuckets = getCutomBucketsFromSiblingAggs(visSchemas.metric); + const customBuckets = getCustomBucketsFromSiblingAggs(visSchemas.metric); // doesn't support sibbling pipeline aggs with different bucket aggs if (customBuckets.length > 1) { From 384ecc4b7ba272b9f890ba102277d0769a730067 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 29 Sep 2022 12:43:47 +0200 Subject: [PATCH 109/185] [Graph] Unskip a11y functional test (#141798) --- .../public/components/settings/url_template_form.tsx | 10 +++++++++- .../workspace_layout/workspace_top_nav_menu.tsx | 3 +++ x-pack/test/accessibility/apps/graph.ts | 7 +++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/graph/public/components/settings/url_template_form.tsx b/x-pack/plugins/graph/public/components/settings/url_template_form.tsx index 57e69d0912a7..4509a003e839 100644 --- a/x-pack/plugins/graph/public/components/settings/url_template_form.tsx +++ b/x-pack/plugins/graph/public/components/settings/url_template_form.tsx @@ -261,7 +261,15 @@ export function UrlTemplateForm(props: UrlTemplateFormProps) { defaultMessage: 'Toolbar icon', })} > -
    +
    {urlTemplateIconChoices.map((icon) => ( { ownFocus: true, className: 'gphSettingsFlyout', maxWidth: 520, + 'aria-label': i18n.translate('xpack.graph.settings.ariaLabel', { + defaultMessage: 'Settings', + }), } ); }, diff --git a/x-pack/test/accessibility/apps/graph.ts b/x-pack/test/accessibility/apps/graph.ts index 85a77f481627..03ca3b2afbfe 100644 --- a/x-pack/test/accessibility/apps/graph.ts +++ b/x-pack/test/accessibility/apps/graph.ts @@ -79,12 +79,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await browser.pressKeys(browser.keys.ESCAPE); }); - // https://github.com/elastic/kibana/issues/134693 - it.skip('Graph settings drilldown tab - add new drilldown', async function () { + it('Graph settings drilldown tab - add new drilldown', async function () { + await testSubjects.click('graphSettingsButton'); + await testSubjects.click('drillDowns'); await testSubjects.click('graphAddNewTemplate'); await a11y.testAppSnapshot(); - await testSubjects.click('graphRemoveUrlTemplate'); - await testSubjects.click('euiFlyoutCloseButton'); await browser.pressKeys(browser.keys.ESCAPE); }); From 57a3162402b54262bb5f730a36082b42535b1fbf Mon Sep 17 00:00:00 2001 From: Luke Gmys Date: Thu, 29 Sep 2022 13:35:59 +0200 Subject: [PATCH 110/185] [TIP] Fix broken setup instructions in the readme (#140519) --- x-pack/plugins/threat_intelligence/FAQ.md | 15 ---- x-pack/plugins/threat_intelligence/README.md | 73 +++++++++++++++----- 2 files changed, 56 insertions(+), 32 deletions(-) delete mode 100644 x-pack/plugins/threat_intelligence/FAQ.md diff --git a/x-pack/plugins/threat_intelligence/FAQ.md b/x-pack/plugins/threat_intelligence/FAQ.md deleted file mode 100644 index d3f128771384..000000000000 --- a/x-pack/plugins/threat_intelligence/FAQ.md +++ /dev/null @@ -1,15 +0,0 @@ -# FAQ - -### Where can I find the UI for the Threat Intelligence plugin? - -Kibana recommends working on a fork of the [elastic/kibana repository](https://github.com/elastic/kibana) (see [here](https://docs.github.com/en/get-started/quickstart/fork-a-repo) to learn about forks). - -### How is the Threat Intelligence code loaded in Kibana? - -The Threat Intelligence plugin is loaded within the [security_solution](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution) plugin. - -### I'm not seeing any data in the Indicators' table - -See this [documentation here](https://github.com/elastic/security-team/blob/main/docs/protections-team/threat-intelligence-services/protections-experience/development-setup.mdx) to get Threat Intelligence feed in Kibana. - -Once you have the feed running, go to `Management > Advanced Settings > Threat indices` and add `filebeat-*` to the list (comma separated). \ No newline at end of file diff --git a/x-pack/plugins/threat_intelligence/README.md b/x-pack/plugins/threat_intelligence/README.md index 945ab9b85a4f..7395ca0df8a7 100755 --- a/x-pack/plugins/threat_intelligence/README.md +++ b/x-pack/plugins/threat_intelligence/README.md @@ -6,28 +6,55 @@ Elastic Threat Intelligence makes it easy to analyze and investigate potential s The Threat Intelligence UI is displayed in Kibana Security, under the Explore section. -## Quick Start +## Development setup -See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. +### Kibana development in general -Verify your node version [here](https://github.com/elastic/kibana/blob/main/.node-version). +Best source - [internal Kibana docs](https://docs.elastic.dev/kibana-dev-docs/getting-started/welcome). If you have any issues with setting up your Kibana dev environment [#kibana](https://elastic.slack.com/archives/C0D8P2XK5) Slack channel is a good way to get help. -**Run ES:** +### Essential `kibana.yml` settings -`yarn es snapshot --license trial` +You can make a copy of `kibana.yml` file into `kibana.dev.yml` and make adjustments to the settings. External documentation on the flags available is [here](https://www.elastic.co/guide/en/kibana/current/settings.html) -**Run Kibana:** +It is recommended to set `server.basePath: "/kbn"` to make you local instance persist the base Kibana path. If you don't do it, the base path will be a random string every time you start Kibana. Any other value than `/kbn` will also work. -> **Important:** -> -> See here to get your `kibana.yaml` to enable the Threat Intelligence plugin. +### Getting Threat Intelligence feeds data into Kibana -``` -yarn kbn reset && yarn kbn bootstrap -yarn start --no-base-path -``` +There are many ways to get data for you local development. We first focus on getting Threat Intelligence data specifically. + +### Setting up filebeat threatintel integrations locally + +1. install [mage](https://github.com/magefile/mage). It is a Go build tool used to build `beats`. Installation from the sources requires Go lang set up. A simpler option might be to install it from a package manager available in your system (eg. `brew` on MacOs) or use their [binary distribution](https://github.com/magefile/mage/releases) +1. start Elasticsearch and Kibana +1. clone [beats](https://github.com/elastic/beats) repository +1. inside beats repository, update `x-pack/filebeat/filebeat.yml` with your local Elasticsearch and Kibana connection configs + + ``` + output.elasticsearch: + hosts: ["localhost:9200"] + username: "elastic" + password: "changeme" + + setup.kibana: + host: "localhost:5601" // make sure to run Kibana with --no-base-path option or specify server.basePath in Kibana config and use it here as a path, eg. localhost:5601/kbn + ``` + +1. go into `x-pack/filebeat` (that's where security related modules live) +1. build filebeat `mage build` +1. enable `threatintel` module by running `./filebeat modules enable threatintel` +1. enable specific Threat Intelligence integrations by updating `modules.d/threatintel.yml`. Update `enable` to `true` in every integration you want to enable and configs specific for these integrations. The bare minimum is to enable Abuse.CH feeds `abuseurl`, `abusemalware` and `malwarebazaar`. +1. run `./filebeat setup -E setup.dashboards.directory=build/kibana` to set up predefined dashboards +1. run `./filebeat -e` to start filebeat +1. to validate that the set up works, wait for some Threat Intel data to be ingested and then go in Analytics > Discover in your local Kibana to search `event.category : threat and event.type : indicator`. You should see some documents returned by this search. Abuse.CH feeds are up to date so you should see the results from the last 7 days. + +### More ways to get data -### Performance +There are many more tools available for getting the data for testing or local development, depending on the data type and usecase. + +- Kibana development docs > [Add data](https://docs.elastic.dev/kibana-dev-docs/getting-started/sample-data) +- [Dev/Design/Testing Environments and Frameworks](https://docs.google.com/document/d/1DGCcLMnVKQ_STlkbS4E0m4kbPivNtR8iMlg_IoCuCEw/edit#) gathered by Security Engineering Productivity team + +### Generate fixtures for local testing You can generate large volumes of threat indicators on demand with the following script: @@ -37,17 +64,29 @@ node scripts/generate_indicators.js see the file in order to adjust the amount of indicators generated. The default is one million. -### Useful hints +## Data for E2E tests -Export local instance data to es_archives (will be loaded in cypress tests). +Use es_archives to export data for e2e testing purposes, like so: ``` TEST_ES_PORT=9200 node scripts/es_archiver save x-pack/test/threat_intelligence_cypress/es_archives/threat_intelligence "logs-ti*" ``` +These can be loaded at will with `x-pack/plugins/threat_intelligence/cypress/tasks/es_archiver.ts` task. + +You can use this approach to load separate data dumps for every test case, to cover all critical scenarios. + ## FAQ -See [FAQ.md](https://github.com/elastic/kibana/blob/main/x-pack/plugins/threat_intelligence/FAQ.md) for questions you may have. +### How is the Threat Intelligence code loaded in Kibana? + +The Threat Intelligence plugin is loaded lazily within the [security_solution](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution) plugin, +from `x-pack/plugins/security_solution/public/threat_intelligence` owned by the Protections Experience Team. + +## QA and demo for implemented features + +One way to QA and demo the feature merged into `main` branch is to run the latest `main` locally. +Another option is to deploy a Staging instance. For Staging environment snapshots are being build every night with the latest state of the `main` branch. More documentation can be found [here](https://cloud.elastic.dev/environments/Staging/#automatic-termination-of-staging-deployments) ## Contributing From 16ca2d28957a833cf78abae97609a3f5a1dfd5c0 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 29 Sep 2022 07:53:50 -0400 Subject: [PATCH 111/185] [Event Log] Adding event log schema check to CI checks (#142104) * Adding event log check to CI check. * Adding event log check to CI check. * Can I check out ECS * Checking out specific ECS branch * Checking out specific ECS branch * Custom error message * Reverting event log mapping test changes * Pinning to 1.8 * Update .buildkite/scripts/steps/checks/event_log.sh Co-authored-by: Jonathan Budzenski Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Jonathan Budzenski --- .buildkite/scripts/common/util.sh | 7 ++++++- .buildkite/scripts/steps/checks.sh | 1 + .buildkite/scripts/steps/checks/event_log.sh | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 .buildkite/scripts/steps/checks/event_log.sh diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index 1ce05856ec6b..748babfc0650 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -39,6 +39,7 @@ check_for_changed_files() { C_RESET='\033[0m' # Reset color SHOULD_AUTO_COMMIT_CHANGES="${2:-}" + CUSTOM_FIX_MESSAGE="${3:-}" GIT_CHANGES="$(git ls-files --modified -- . ':!:.bazelrc')" if [ "$GIT_CHANGES" ]; then @@ -75,7 +76,11 @@ check_for_changed_files() { else echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" echo -e "$GIT_CHANGES\n" - echo -e "\n${YELLOW}TO FIX: Run '$1' locally, commit the changes and push to your branch${C_RESET}\n" + if [ "$CUSTOM_FIX_MESSAGE" ]; then + echo "$CUSTOM_FIX_MESSAGE" + else + echo -e "\n${YELLOW}TO FIX: Run '$1' locally, commit the changes and push to your branch${C_RESET}\n" + fi exit 1 fi fi diff --git a/.buildkite/scripts/steps/checks.sh b/.buildkite/scripts/steps/checks.sh index 4af63d318c80..0e11ac04eea1 100755 --- a/.buildkite/scripts/steps/checks.sh +++ b/.buildkite/scripts/steps/checks.sh @@ -8,6 +8,7 @@ export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/steps/checks/precommit_hook.sh .buildkite/scripts/steps/checks/ftr_configs.sh .buildkite/scripts/steps/checks/bazel_packages.sh +.buildkite/scripts/steps/checks/event_log.sh .buildkite/scripts/steps/checks/telemetry.sh .buildkite/scripts/steps/checks/ts_projects.sh .buildkite/scripts/steps/checks/jest_configs.sh diff --git a/.buildkite/scripts/steps/checks/event_log.sh b/.buildkite/scripts/steps/checks/event_log.sh new file mode 100755 index 000000000000..dc9c01902c01 --- /dev/null +++ b/.buildkite/scripts/steps/checks/event_log.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +echo --- Check Event Log Schema + +# event log schema is pinned to a specific version of ECS +ECS_STABLE_VERSION=1.8 +git clone --depth 1 -b $ECS_STABLE_VERSION https://github.com/elastic/ecs.git ../ecs + +node x-pack/plugins/event_log/scripts/create_schemas.js + +check_for_changed_files 'node x-pack/plugins/event_log/scripts/create_schemas.js' false 'Follow the directions in x-pack/plugins/event_log/generated/README.md to make schema changes for the event log.' From 13824dd0ddbb88bcbd8995fab85ee325ffa6ae4f Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Thu, 29 Sep 2022 13:57:16 +0200 Subject: [PATCH 112/185] added retry_on_conflict and report errors from bulk agent update (#142088) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet/server/services/agents/crud.ts | 1 + .../server/services/agents/reassign.test.ts | 25 +++++++++++++++++++ .../services/agents/reassign_action_runner.ts | 8 +++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index 305bf2b6bacb..55a244664238 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -405,6 +405,7 @@ export async function bulkUpdateAgents( { update: { _id: agentId, + retry_on_conflict: 3, }, }, { diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.test.ts b/x-pack/plugins/fleet/server/services/agents/reassign.test.ts index a54c2cb56c94..3fb9d2ee1f33 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.test.ts @@ -96,4 +96,29 @@ describe('reassignAgents (plural)', () => { }); expect(calledWithActionResults.body?.[1] as any).toEqual(expectedObject); }); + + it('should report errors from ES agent update call', async () => { + const { soClient, esClient, agentInRegularDoc, regularAgentPolicySO2 } = createClientMock(); + esClient.bulk.mockResponse({ + items: [ + { + update: { + _id: agentInRegularDoc._id, + error: new Error('version conflict'), + }, + }, + ], + } as any); + const idsToReassign = [agentInRegularDoc._id]; + await reassignAgents(soClient, esClient, { agentIds: idsToReassign }, regularAgentPolicySO2.id); + + const calledWithActionResults = esClient.bulk.mock.calls[1][0] as estypes.BulkRequest; + const expectedObject = expect.objectContaining({ + '@timestamp': expect.anything(), + action_id: expect.anything(), + agent_id: agentInRegularDoc._id, + error: 'version conflict', + }); + expect(calledWithActionResults.body?.[1] as any).toEqual(expectedObject); + }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts index 55c0e00728d1..96405e464b35 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts @@ -72,7 +72,7 @@ export async function reassignBatch( throw new AgentReassignmentError('No agents to reassign, already assigned or hosted agents'); } - await bulkUpdateAgents( + const res = await bulkUpdateAgents( esClient, agentsToUpdate.map((agent) => ({ agentId: agent.id, @@ -83,6 +83,12 @@ export async function reassignBatch( })) ); + res.items + .filter((item) => !item.success) + .forEach((item) => { + errors[item.id] = item.error!; + }); + const actionId = options.actionId ?? uuid(); const errorCount = Object.keys(errors).length; const total = options.total ?? agentsToUpdate.length + errorCount; From 60e5e411070d80815fbbf0f054bf25035dd2770e Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Thu, 29 Sep 2022 14:23:50 +0200 Subject: [PATCH 113/185] Fix Legend Options and Node Map layout on mobile view (#142052) * Fix Legend Options and Node Map layout on mobile view * Fix Number of Color bar on mobile Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/waffle/legend_controls.tsx | 15 +++++++++++++-- .../inventory_view/components/waffle/map.tsx | 1 + .../components/waffle/palette_preview.tsx | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx index a9b8b399a683..77784f855de3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx @@ -24,6 +24,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import React, { SyntheticEvent, useState, useCallback, useEffect } from 'react'; import { first, last } from 'lodash'; import { InfraWaffleMapBounds, InventoryColorPalette, PALETTES } from '../../../../../lib/lib'; @@ -189,9 +190,10 @@ export const LegendControls = ({ button={buttonComponent} anchorPosition="leftCenter" data-test-subj="legendControls" + // panelStyle={{ width: '100%', maxWidth: 375 }} > Legend Options - + - + ); }; + +const StyledEuiForm = euiStyled(EuiForm)` + min-width: 400px; + @media (max-width: 480px) { + min-width: 100%; + max-width: 100%; + width: 100vw; + } +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx index e0d03284d63f..a4558d6a7e9b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx @@ -105,6 +105,7 @@ const WaffleMapOuterContainer = euiStyled.div<{ bottomMargin: number; staticHeig overflow-x: hidden; overflow-y: auto; margin-bottom: ${(props) => props.bottomMargin}px; + max-width: calc(100vw - 90px); ${(props) => props.staticHeight && 'min-height: 300px;'} `; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx index 132982eae69b..9dc5f3acd6e5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx @@ -28,9 +28,9 @@ export const PalettePreview = ({ steps, palette, reverse }: Props) => { }; const Swatch = euiStyled.div` - width: 15px; + max-width: 15px; height: 12px; - flex: 0 0 auto; + flex: 1 1 auto; &:first-child { border-radius: ${(props) => props.theme.eui.euiBorderRadius} 0 0 ${(props) => props.theme.eui.euiBorderRadius}; From e678b7888f64d7b8b80fdeb128c6ac96f87d3709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Thu, 29 Sep 2022 14:32:29 +0200 Subject: [PATCH 114/185] Adds ML URL shortcut to doc link manager (#142091) Co-authored-by: Lisa Cawley --- packages/kbn-doc-links/src/get_doc_links.ts | 53 +++++++++++---------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 721fc431a3c2..3a0b89c1f0d1 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -31,6 +31,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { const ENTERPRISE_SEARCH_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/enterprise-search/${DOC_LINK_VERSION}/`; const WORKPLACE_SEARCH_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/workplace-search/${DOC_LINK_VERSION}/`; const SEARCH_UI_DOCS = `${DOCS_WEBSITE_URL}search-ui/`; + const MACHINE_LEARNING_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/`; return deepFreeze({ settings: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/settings.html`, @@ -398,39 +399,39 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { savedObjectsApiList: `${KIBANA_DOCS}saved-objects-api.html#saved-objects-api`, }, ml: { - guide: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/index.html`, - aggregations: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-aggregation.html`, - anomalyDetection: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-overview.html`, - anomalyDetectionJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html`, - anomalyDetectionConfiguringCategories: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-categories.html`, - anomalyDetectionBucketSpan: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-bucket-span`, - anomalyDetectionCardinality: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-cardinality`, - anomalyDetectionCreateJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-create-job`, - anomalyDetectionDetectors: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-detectors`, - anomalyDetectionInfluencers: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-influencers`, + guide: `${MACHINE_LEARNING_DOCS}index.html`, + aggregations: `${MACHINE_LEARNING_DOCS}ml-configuring-aggregation.html`, + anomalyDetection: `${MACHINE_LEARNING_DOCS}ml-ad-overview.html`, + anomalyDetectionJobs: `${MACHINE_LEARNING_DOCS}ml-ad-finding-anomalies.html`, + anomalyDetectionConfiguringCategories: `${MACHINE_LEARNING_DOCS}ml-configuring-categories.html`, + anomalyDetectionBucketSpan: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-bucket-span`, + anomalyDetectionCardinality: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-cardinality`, + anomalyDetectionCreateJobs: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-create-job`, + anomalyDetectionDetectors: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-detectors`, + anomalyDetectionInfluencers: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-influencers`, anomalyDetectionJobResource: `${ELASTICSEARCH_DOCS}ml-put-job.html#ml-put-job-path-parms`, anomalyDetectionJobResourceAnalysisConfig: `${ELASTICSEARCH_DOCS}ml-put-job.html#put-analysisconfig`, - anomalyDetectionJobTips: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-job-tips`, - alertingRules: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-alerts.html`, - anomalyDetectionModelMemoryLimits: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-model-memory-limits`, - calendars: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-calendars`, - classificationEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-classification.html#ml-dfanalytics-classification-evaluation`, - customRules: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-run-jobs.html#ml-ad-rules`, - customUrls: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-url.html`, - dataFrameAnalytics: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics.html`, - dFAPrepareData: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-overview.html#prepare-transform-data`, - featureImportance: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-feature-importance.html`, - outlierDetectionRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-finding-outliers.html#ml-dfanalytics-roc`, - regressionEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-regression.html#ml-dfanalytics-regression-evaluation`, - classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-classification.html#ml-dfanalytics-class-aucroc`, + anomalyDetectionJobTips: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-job-tips`, + alertingRules: `${MACHINE_LEARNING_DOCS}ml-configuring-alerts.html`, + anomalyDetectionModelMemoryLimits: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-model-memory-limits`, + calendars: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-calendars`, + classificationEvaluation: `${MACHINE_LEARNING_DOCS}ml-dfa-classification.html#ml-dfanalytics-classification-evaluation`, + customRules: `${MACHINE_LEARNING_DOCS}ml-ad-run-jobs.html#ml-ad-rules`, + customUrls: `${MACHINE_LEARNING_DOCS}ml-configuring-url.html`, + dataFrameAnalytics: `${MACHINE_LEARNING_DOCS}ml-dfanalytics.html`, + dFAPrepareData: `${MACHINE_LEARNING_DOCS}ml-dfa-overview.html#prepare-transform-data`, + featureImportance: `${MACHINE_LEARNING_DOCS}ml-feature-importance.html`, + outlierDetectionRoc: `${MACHINE_LEARNING_DOCS}ml-dfa-finding-outliers.html#ml-dfanalytics-roc`, + regressionEvaluation: `${MACHINE_LEARNING_DOCS}ml-dfa-regression.html#ml-dfanalytics-regression-evaluation`, + classificationAucRoc: `${MACHINE_LEARNING_DOCS}ml-dfa-classification.html#ml-dfanalytics-class-aucroc`, setUpgradeMode: `${ELASTICSEARCH_DOCS}ml-set-upgrade-mode.html`, - trainedModels: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-trained-models.html`, - startTrainedModelsDeployment: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-nlp-deploy-models.html#ml-nlp-deploy-model`, + trainedModels: `${MACHINE_LEARNING_DOCS}ml-trained-models.html`, + startTrainedModelsDeployment: `${MACHINE_LEARNING_DOCS}ml-nlp-deploy-models.html#ml-nlp-deploy-model`, }, transforms: { guide: `${ELASTICSEARCH_DOCS}transforms.html`, // TODO add valid docs URL - alertingRules: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-alerts.html`, + alertingRules: `${MACHINE_LEARNING_DOCS}ml-configuring-alerts.html`, }, visualize: { guide: `${KIBANA_DOCS}dashboard.html`, From 7c9875c33c1922bb0a6c4ca436e25dd7f0c95ea7 Mon Sep 17 00:00:00 2001 From: doakalexi <109488926+doakalexi@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:56:47 -0400 Subject: [PATCH 115/185] [Response Ops] [Alerting] Rules and Connectors: Current page in breadcrumbs shows as link (#141838) * Updating breadcrumb function for alerting * Remove href --- .../public/application/lib/breadcrumb.test.ts | 31 +++++++++------- .../public/application/lib/breadcrumb.ts | 35 +++++++++++-------- .../rule_details/components/rule_details.tsx | 7 ++-- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.test.ts index 26a69093ae48..cbe226ce68aa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.test.ts @@ -5,40 +5,47 @@ * 2.0. */ -import { getAlertingSectionBreadcrumb, getRuleDetailsBreadcrumb } from './breadcrumb'; +import { getAlertingSectionBreadcrumb } from './breadcrumb'; import { i18n } from '@kbn/i18n'; import { routeToConnectors, routeToRules, routeToHome } from '../constants'; describe('getAlertingSectionBreadcrumb', () => { test('if change calls return proper breadcrumb title ', async () => { - expect(getAlertingSectionBreadcrumb('connectors')).toMatchObject({ + expect(getAlertingSectionBreadcrumb('connectors', true)).toMatchObject({ text: i18n.translate('xpack.triggersActionsUI.connectors.breadcrumbTitle', { defaultMessage: 'Connectors', }), href: `${routeToConnectors}`, }); - expect(getAlertingSectionBreadcrumb('rules')).toMatchObject({ + expect(getAlertingSectionBreadcrumb('rules', true)).toMatchObject({ text: i18n.translate('xpack.triggersActionsUI.rules.breadcrumbTitle', { defaultMessage: 'Rules', }), href: `${routeToRules}`, }); - expect(getAlertingSectionBreadcrumb('home')).toMatchObject({ + expect(getAlertingSectionBreadcrumb('home', true)).toMatchObject({ text: i18n.translate('xpack.triggersActionsUI.home.breadcrumbTitle', { defaultMessage: 'Rules and Connectors', }), href: `${routeToHome}`, }); }); -}); - -describe('getRuleDetailsBreadcrumb', () => { - test('if select an alert should return proper breadcrumb title with alert name ', async () => { - expect(getRuleDetailsBreadcrumb('testId', 'testName')).toMatchObject({ - text: i18n.translate('xpack.triggersActionsUI.alertDetails.breadcrumbTitle', { - defaultMessage: 'testName', + test('if boolean is passed in returns proper breadcrumb href ', async () => { + expect(getAlertingSectionBreadcrumb('connectors', true)).toMatchObject({ + text: i18n.translate('xpack.triggersActionsUI.connectors.breadcrumbTitle', { + defaultMessage: 'Connectors', + }), + href: `${routeToConnectors}`, + }); + expect(getAlertingSectionBreadcrumb('rules', false)).toMatchObject({ + text: i18n.translate('xpack.triggersActionsUI.rules.breadcrumbTitle', { + defaultMessage: 'Rules', + }), + }); + expect(getAlertingSectionBreadcrumb('home', false)).toMatchObject({ + text: i18n.translate('xpack.triggersActionsUI.home.breadcrumbTitle', { + defaultMessage: 'Rules and Connectors', }), - href: '/rule/testId', }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts index d26cdbcb3d17..46a15b12bb73 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/breadcrumb.ts @@ -6,9 +6,12 @@ */ import { i18n } from '@kbn/i18n'; -import { routeToHome, routeToConnectors, routeToRules, routeToRuleDetails } from '../constants'; +import { routeToHome, routeToConnectors, routeToRules } from '../constants'; -export const getAlertingSectionBreadcrumb = (type: string): { text: string; href: string } => { +export const getAlertingSectionBreadcrumb = ( + type: string, + returnHref: boolean = false +): { text: string; href?: string } => { // Home and sections switch (type) { case 'connectors': @@ -16,31 +19,33 @@ export const getAlertingSectionBreadcrumb = (type: string): { text: string; href text: i18n.translate('xpack.triggersActionsUI.connectors.breadcrumbTitle', { defaultMessage: 'Connectors', }), - href: `${routeToConnectors}`, + ...(returnHref + ? { + href: `${routeToConnectors}`, + } + : {}), }; case 'rules': return { text: i18n.translate('xpack.triggersActionsUI.rules.breadcrumbTitle', { defaultMessage: 'Rules', }), - href: `${routeToRules}`, + ...(returnHref + ? { + href: `${routeToRules}`, + } + : {}), }; default: return { text: i18n.translate('xpack.triggersActionsUI.home.breadcrumbTitle', { defaultMessage: 'Rules and Connectors', }), - href: `${routeToHome}`, + ...(returnHref + ? { + href: `${routeToHome}`, + } + : {}), }; } }; - -export const getRuleDetailsBreadcrumb = ( - id: string, - name: string -): { text: string; href: string } => { - return { - text: name, - href: `${routeToRuleDetails.replace(':ruleId', id)}`, - }; -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx index c17bc4008666..38357d7c5605 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx @@ -36,7 +36,7 @@ import { hasExecuteActionsCapability, hasManageApiKeysCapability, } from '../../../lib/capabilities'; -import { getAlertingSectionBreadcrumb, getRuleDetailsBreadcrumb } from '../../../lib/breadcrumb'; +import { getAlertingSectionBreadcrumb } from '../../../lib/breadcrumb'; import { getCurrentDocTitle } from '../../../lib/doc_title'; import { Rule, @@ -117,10 +117,7 @@ export const RuleDetails: React.FunctionComponent = ({ // Set breadcrumb and page title useEffect(() => { - setBreadcrumbs([ - getAlertingSectionBreadcrumb('rules'), - getRuleDetailsBreadcrumb(rule.id, rule.name), - ]); + setBreadcrumbs([getAlertingSectionBreadcrumb('rules', true), { text: rule.name }]); chrome.docTitle.change(getCurrentDocTitle('rules')); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); From b2ab340425da35fb9f0f3627d3bde5d55c9ae3ea Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 29 Sep 2022 09:40:41 -0400 Subject: [PATCH 116/185] [Security Solution][Endpoint] Move all endpoint responder jest tests for response actions to (jest) integration tests (#142006) * un-skip tests in endpoint_responder * move endpoint responder tests to integration tests * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * Add jest integration config file * add standard setup for react testing `data-test-subj` testIdAttribute * change `@kbn/test/jest_integration` preset to include all `setupFilesAfterEnv` from base preset * Fix jest testing warning/error about missing react `key` in command_usage.tsx component * Stabilise test when checking calls to action details api Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../kbn-test/jest_integration/jest-preset.js | 3 +-- .../jest.integration.config.js | 12 +++++++++ .../console/components/command_usage.tsx | 4 +-- .../get_processes_action.test.tsx | 25 ++++++++--------- .../isolate_action.test.tsx | 27 ++++++++++--------- .../kill_process_action.test.tsx | 27 ++++++++++--------- .../release_action.test.tsx | 27 ++++++++++--------- .../suspend_process_action.test.tsx | 25 ++++++++--------- 8 files changed, 84 insertions(+), 66 deletions(-) create mode 100644 x-pack/plugins/security_solution/jest.integration.config.js rename x-pack/plugins/security_solution/public/management/components/endpoint_responder/{ => integration_tests}/get_processes_action.test.tsx (88%) rename x-pack/plugins/security_solution/public/management/components/endpoint_responder/{ => integration_tests}/isolate_action.test.tsx (87%) rename x-pack/plugins/security_solution/public/management/components/endpoint_responder/{ => integration_tests}/kill_process_action.test.tsx (91%) rename x-pack/plugins/security_solution/public/management/components/endpoint_responder/{ => integration_tests}/release_action.test.tsx (88%) rename x-pack/plugins/security_solution/public/management/components/endpoint_responder/{ => integration_tests}/suspend_process_action.test.tsx (91%) diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index b039329893d3..d150f8bd9e45 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -15,9 +15,8 @@ module.exports = { (pattern) => !pattern.includes('integration_tests') ), setupFilesAfterEnv: [ + ...preset.setupFilesAfterEnv, '/node_modules/@kbn/test/target_node/src/jest/setup/after_env.integration.js', - '/node_modules/@kbn/test/target_node/src/jest/setup/mocks.moment_timezone.js', - '/node_modules/@kbn/test/target_node/src/jest/setup/mocks.eui.js', ], reporters: [ 'default', diff --git a/x-pack/plugins/security_solution/jest.integration.config.js b/x-pack/plugins/security_solution/jest.integration.config.js new file mode 100644 index 000000000000..046faf5e6198 --- /dev/null +++ b/x-pack/plugins/security_solution/jest.integration.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test/jest_integration', + rootDir: '../../..', + roots: ['/x-pack/plugins/security_solution'], +}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx index e70383d8251c..a79cc3cd7ad5 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx @@ -22,10 +22,10 @@ export const CommandInputUsage = memo>(({ const usageHelp = useMemo(() => { return getArgumentsForCommand(commandDef).map((usage, index) => { return ( - <> + {index > 0 && } {`${commandDef.name} ${usage}`} - + ); }); }, [commandDef]); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/get_processes_action.test.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/get_processes_action.test.tsx index 97689a790afa..3b46967c33ab 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/get_processes_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/get_processes_action.test.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { ConsoleManagerTestComponent, getConsoleManagerMockRenderResultQueriesAndActions, -} from '../console/components/console_manager/mocks'; +} from '../../console/components/console_manager/mocks'; import React from 'react'; -import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_actions_console_commands'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import { enterConsoleCommand } from '../console/mocks'; +import { getEndpointResponseActionsConsoleCommands } from '../endpoint_response_actions_console_commands'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; +import { enterConsoleCommand } from '../../console/mocks'; import { waitFor } from '@testing-library/react'; -import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; -import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; +import type { ResponderCapabilities } from '../../../../../common/endpoint/constants'; +import { RESPONDER_CAPABILITIES } from '../../../../../common/endpoint/constants'; describe('When using processes action from response actions console', () => { let render: ( @@ -161,6 +161,7 @@ describe('When using processes action from response actions console', () => { await waitFor(() => { expect(apiMocks.responseProvider.processes).toHaveBeenCalledTimes(1); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); }); // Hide the console @@ -182,20 +183,20 @@ describe('When using processes action from response actions console', () => { path: '/api/endpoint/action/1.2.3', }); pendingDetailResponse.data.isCompleted = false; + apiMocks.responseProvider.actionDetails.mockClear(); apiMocks.responseProvider.actionDetails.mockReturnValue(pendingDetailResponse); await render(); - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); await consoleManagerMockAccess.openRunningConsole(); await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(3); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); }); }); - // FLAKY: https://github.com/elastic/kibana/issues/139707 - it.skip('should display completion output if done (no additional API calls)', async () => { + it('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/isolate_action.test.tsx similarity index 87% rename from x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/isolate_action.test.tsx index 110ddbb53b1b..10dde22e6083 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/isolate_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/isolate_action.test.tsx @@ -5,20 +5,20 @@ * 2.0. */ -import type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { ConsoleManagerTestComponent, getConsoleManagerMockRenderResultQueriesAndActions, -} from '../console/components/console_manager/mocks'; +} from '../../console/components/console_manager/mocks'; import React from 'react'; -import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_actions_console_commands'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import { enterConsoleCommand } from '../console/mocks'; +import { getEndpointResponseActionsConsoleCommands } from '../endpoint_response_actions_console_commands'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; +import { enterConsoleCommand } from '../../console/mocks'; import { waitFor } from '@testing-library/react'; -import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; -import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; -import { getDeferred } from '../../mocks/utils'; +import type { ResponderCapabilities } from '../../../../../common/endpoint/constants'; +import { RESPONDER_CAPABILITIES } from '../../../../../common/endpoint/constants'; +import { getDeferred } from '../../../mocks/utils'; describe('When using isolate action from response actions console', () => { let render: ( @@ -170,6 +170,7 @@ describe('When using isolate action from response actions console', () => { await waitFor(() => { expect(apiMocks.responseProvider.isolateHost).toHaveBeenCalledTimes(1); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); }); // Hide the console @@ -191,20 +192,20 @@ describe('When using isolate action from response actions console', () => { path: '/api/endpoint/action/1.2.3', }); pendingDetailResponse.data.isCompleted = false; + apiMocks.responseProvider.actionDetails.mockClear(); apiMocks.responseProvider.actionDetails.mockReturnValue(pendingDetailResponse); await render(); - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); await consoleManagerMockAccess.openRunningConsole(); await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(3); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); }); }); - // SKIP: https://github.com/elastic/kibana/issues/139586 - it.skip('should display completion output if done (no additional API calls)', async () => { + it('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/kill_process_action.test.tsx similarity index 91% rename from x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/kill_process_action.test.tsx index f888df2099b1..12cec2dd613b 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/kill_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/kill_process_action.test.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { ConsoleManagerTestComponent, getConsoleManagerMockRenderResultQueriesAndActions, -} from '../console/components/console_manager/mocks'; +} from '../../console/components/console_manager/mocks'; import React from 'react'; -import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_actions_console_commands'; -import { enterConsoleCommand } from '../console/mocks'; +import { getEndpointResponseActionsConsoleCommands } from '../endpoint_response_actions_console_commands'; +import { enterConsoleCommand } from '../../console/mocks'; import { waitFor } from '@testing-library/react'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; -import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; +import type { ResponderCapabilities } from '../../../../../common/endpoint/constants'; +import { RESPONDER_CAPABILITIES } from '../../../../../common/endpoint/constants'; describe('When using the kill-process action from response actions console', () => { let render: ( @@ -250,6 +250,7 @@ describe('When using the kill-process action from response actions console', () await waitFor(() => { expect(apiMocks.responseProvider.killProcess).toHaveBeenCalledTimes(1); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); }); // Hide the console @@ -270,21 +271,23 @@ describe('When using the kill-process action from response actions console', () const pendingDetailResponse = apiMocks.responseProvider.actionDetails({ path: '/api/endpoint/action/1.2.3', }); + pendingDetailResponse.data.isCompleted = false; + apiMocks.responseProvider.actionDetails.mockClear(); apiMocks.responseProvider.actionDetails.mockReturnValue(pendingDetailResponse); + await render(); - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); await consoleManagerMockAccess.openRunningConsole(); await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(3); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); }); }); - // FLAKY: https://github.com/elastic/kibana/issues/139962 - it.skip('should display completion output if done (no additional API calls)', async () => { + it('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/release_action.test.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/release_action.test.tsx index d1c1ea264f86..c2052a4e6401 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/release_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/release_action.test.tsx @@ -5,20 +5,20 @@ * 2.0. */ -import type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { ConsoleManagerTestComponent, getConsoleManagerMockRenderResultQueriesAndActions, -} from '../console/components/console_manager/mocks'; +} from '../../console/components/console_manager/mocks'; import React from 'react'; -import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_actions_console_commands'; -import { enterConsoleCommand } from '../console/mocks'; +import { getEndpointResponseActionsConsoleCommands } from '../endpoint_response_actions_console_commands'; +import { enterConsoleCommand } from '../../console/mocks'; import { waitFor } from '@testing-library/react'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; -import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; -import { getDeferred } from '../../mocks/utils'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; +import type { ResponderCapabilities } from '../../../../../common/endpoint/constants'; +import { RESPONDER_CAPABILITIES } from '../../../../../common/endpoint/constants'; +import { getDeferred } from '../../../mocks/utils'; describe('When using the release action from response actions console', () => { let render: ( @@ -171,6 +171,7 @@ describe('When using the release action from response actions console', () => { await waitFor(() => { expect(apiMocks.responseProvider.releaseHost).toHaveBeenCalledTimes(1); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); }); // Hide the console @@ -192,20 +193,20 @@ describe('When using the release action from response actions console', () => { path: '/api/endpoint/action/1.2.3', }); pendingDetailResponse.data.isCompleted = false; + apiMocks.responseProvider.actionDetails.mockClear(); apiMocks.responseProvider.actionDetails.mockReturnValue(pendingDetailResponse); await render(); - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); await consoleManagerMockAccess.openRunningConsole(); await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(3); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); }); }); - // FLAKY: https://github.com/elastic/kibana/issues/139641 - it.skip('should display completion output if done (no additional API calls)', async () => { + it('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/suspend_process_action.test.tsx similarity index 91% rename from x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/suspend_process_action.test.tsx index 7479e52edfb0..28fe91999662 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/suspend_process_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/integration_tests/suspend_process_action.test.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { ConsoleManagerTestComponent, getConsoleManagerMockRenderResultQueriesAndActions, -} from '../console/components/console_manager/mocks'; +} from '../../console/components/console_manager/mocks'; import React from 'react'; -import { getEndpointResponseActionsConsoleCommands } from './endpoint_response_actions_console_commands'; -import { enterConsoleCommand } from '../console/mocks'; +import { getEndpointResponseActionsConsoleCommands } from '../endpoint_response_actions_console_commands'; +import { enterConsoleCommand } from '../../console/mocks'; import { waitFor } from '@testing-library/react'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import type { ResponderCapabilities } from '../../../../common/endpoint/constants'; -import { RESPONDER_CAPABILITIES } from '../../../../common/endpoint/constants'; +import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; +import type { ResponderCapabilities } from '../../../../../common/endpoint/constants'; +import { RESPONDER_CAPABILITIES } from '../../../../../common/endpoint/constants'; describe('When using the suspend-process action from response actions console', () => { let render: ( @@ -241,6 +241,7 @@ describe('When using the suspend-process action from response actions console', await waitFor(() => { expect(apiMocks.responseProvider.suspendProcess).toHaveBeenCalledTimes(1); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); }); // Hide the console @@ -262,20 +263,20 @@ describe('When using the suspend-process action from response actions console', path: '/api/endpoint/action/1.2.3', }); pendingDetailResponse.data.isCompleted = false; + apiMocks.responseProvider.actionDetails.mockClear(); apiMocks.responseProvider.actionDetails.mockReturnValue(pendingDetailResponse); await render(); - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); await consoleManagerMockAccess.openRunningConsole(); await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(3); + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(2); }); }); - // FLAKY: https://github.com/elastic/kibana/issues/140119 - it.skip('should display completion output if done (no additional API calls)', async () => { + it('should display completion output if done (no additional API calls)', async () => { await render(); expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledTimes(1); From db6a82530b00914f3e03f414a71707d7f942a755 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 29 Sep 2022 15:44:53 +0200 Subject: [PATCH 117/185] [Security Solution] Adds Cypress test for index action in detection rules (#141069) Co-authored-by: Dmitrii Shevchenko Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../detection_rules/custom_query_rule.cy.ts | 30 ++- .../custom_query_rule_data_view.cy.ts | 24 +- .../custom_saved_query_rule.cy.ts | 6 +- .../event_correlation_rule.cy.ts | 21 +- .../indicator_match_rule.cy.ts | 16 +- .../e2e/detection_rules/new_terms_rule.cy.ts | 16 +- .../e2e/detection_rules/override.cy.ts | 21 +- .../e2e/detection_rules/rule_actions.cy.ts | 74 ++++++ .../e2e/detection_rules/threshold_rule.cy.ts | 20 +- .../cypress/objects/connector.ts | 24 +- .../security_solution/cypress/objects/rule.ts | 41 ++- .../cypress/screens/create_new_rule.ts | 8 + .../cypress/tasks/api_calls/elasticsearch.ts | 55 ++++ .../cypress/tasks/api_calls/rules.ts | 94 ++++--- .../security_solution/cypress/tasks/common.ts | 43 ++++ .../cypress/tasks/create_new_rule.ts | 235 +++++++++++------- .../json_editor_with_message_variables.tsx | 1 + 17 files changed, 533 insertions(+), 196 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts create mode 100644 x-pack/plugins/security_solution/cypress/tasks/api_calls/elasticsearch.ts diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index bdc76cc5f0a5..a594d7f3ffc2 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -6,6 +6,7 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getNewRule, getExistingRule, @@ -13,6 +14,7 @@ import { getEditedRule, getNewOverrideRule, } from '../../objects/rule'; +import type { CompleteTimeline } from '../../objects/timeline'; import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { @@ -46,6 +48,7 @@ import { FROM_VALIDATION_ERROR, EMAIL_ACTION_TO_INPUT, EMAIL_ACTION_SUBJECT_INPUT, + SCHEDULE_CONTINUE_BUTTON, } from '../../screens/create_new_rule'; import { ADDITIONAL_LOOK_BACK_DETAILS, @@ -87,7 +90,7 @@ import { createAndEnableRule, fillAboutRule, fillAboutRuleAndContinue, - fillDefineCustomRuleWithImportedQueryAndContinue, + fillDefineCustomRuleAndContinue, fillEmailConnectorForm, fillScheduleRuleAndContinue, goToAboutStepTab, @@ -108,19 +111,21 @@ describe('Custom query rules', () => { login(); }); describe('Custom detection rules creation', () => { - const expectedUrls = getNewRule().referenceUrls.join(''); - const expectedFalsePositives = getNewRule().falsePositivesExamples.join(''); - const expectedTags = getNewRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getNewRule().mitre); + const expectedUrls = getNewRule().referenceUrls?.join(''); + const expectedFalsePositives = getNewRule().falsePositivesExamples?.join(''); + const expectedTags = getNewRule().tags?.join(''); + const mitreAttack = getNewRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; beforeEach(() => { + const timeline = getNewRule().timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(getNewRule().timeline).then((response) => { + createTimeline(timeline).then((response) => { cy.wrap({ ...getNewRule(), timeline: { - ...getNewRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); @@ -129,7 +134,7 @@ describe('Custom query rules', () => { it('Creates and enables a new rule', function () { visit(RULE_CREATION); - fillDefineCustomRuleWithImportedQueryAndContinue(this.rule); + fillDefineCustomRuleAndContinue(this.rule); fillAboutRuleAndContinue(this.rule); fillScheduleRuleAndContinue(this.rule); @@ -144,6 +149,7 @@ describe('Custom query rules', () => { cy.get(RULE_NAME_INPUT).invoke('val').should('eql', this.rule.name); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); + cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); createAndEnableRule(); @@ -182,11 +188,11 @@ describe('Custom query rules', () => { cy.get(SCHEDULE_DETAILS).within(() => { getDetails(RUNS_EVERY_DETAILS).should( 'have.text', - `${getNewRule().runsEvery.interval}${getNewRule().runsEvery.type}` + `${getNewRule().runsEvery?.interval}${getNewRule().runsEvery?.type}` ); getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( 'have.text', - `${getNewRule().lookBack.interval}${getNewRule().lookBack.type}` + `${getNewRule().lookBack?.interval}${getNewRule().lookBack?.type}` ); }); @@ -299,7 +305,7 @@ describe('Custom query rules', () => { context('Edition', () => { const rule = getEditedRule(); - const expectedEditedtags = rule.tags.join(''); + const expectedEditedtags = rule.tags?.join(''); const expectedEditedIndexPatterns = rule.dataSource.type === 'indexPatterns' && rule.dataSource.index && @@ -349,7 +355,7 @@ describe('Custom query rules', () => { // expect about step to populate cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name); cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description); - cy.get(TAGS_FIELD).should('have.text', existingRule.tags.join('')); + cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join('')); cy.get(SEVERITY_DROPDOWN).should('have.text', existingRule.severity); cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', existingRule.riskScore); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts index 77a4ae274e6e..727c7257b668 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts @@ -6,7 +6,9 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getDataViewRule } from '../../objects/rule'; +import type { CompleteTimeline } from '../../objects/timeline'; import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { @@ -50,7 +52,7 @@ import { postDataView } from '../../tasks/common'; import { createAndEnableRule, fillAboutRuleAndContinue, - fillDefineCustomRuleWithImportedQueryAndContinue, + fillDefineCustomRuleAndContinue, fillScheduleRuleAndContinue, waitForAlertsToPopulate, waitForTheRuleToBeExecuted, @@ -69,10 +71,11 @@ describe('Custom query rules', () => { describe('Custom detection rules creation with data views', () => { const rule = getDataViewRule(); - const expectedUrls = rule.referenceUrls.join(''); - const expectedFalsePositives = rule.falsePositivesExamples.join(''); - const expectedTags = rule.tags.join(''); - const expectedMitre = formatMitreAttackDescription(rule.mitre); + const expectedUrls = rule.referenceUrls?.join(''); + const expectedFalsePositives = rule.falsePositivesExamples?.join(''); + const expectedTags = rule.tags?.join(''); + const mitreAttack = rule.mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; beforeEach(() => { @@ -80,12 +83,13 @@ describe('Custom query rules', () => { are creating a data view we'll use after and cleanKibana does not delete all the data views created, esArchiverReseKibana does. We don't use esArchiverReseKibana in all the tests because is a time-consuming method and we don't need to perform an exhaustive cleaning in all the other tests. */ + const timeline = rule.timeline as CompleteTimeline; esArchiverResetKibana(); - createTimeline(rule.timeline).then((response) => { + createTimeline(timeline).then((response) => { cy.wrap({ ...rule, timeline: { - ...rule.timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); @@ -97,7 +101,7 @@ describe('Custom query rules', () => { it('Creates and enables a new rule', function () { visit(RULE_CREATION); - fillDefineCustomRuleWithImportedQueryAndContinue(this.rule); + fillDefineCustomRuleAndContinue(this.rule); fillAboutRuleAndContinue(this.rule); fillScheduleRuleAndContinue(this.rule); createAndEnableRule(); @@ -138,11 +142,11 @@ describe('Custom query rules', () => { cy.get(SCHEDULE_DETAILS).within(() => { getDetails(RUNS_EVERY_DETAILS).should( 'have.text', - `${getDataViewRule().runsEvery.interval}${getDataViewRule().runsEvery.type}` + `${getDataViewRule().runsEvery?.interval}${getDataViewRule().runsEvery?.type}` ); getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( 'have.text', - `${getDataViewRule().lookBack.interval}${getDataViewRule().lookBack.type}` + `${getDataViewRule().lookBack?.interval}${getDataViewRule().lookBack?.type}` ); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts index 5027fe09a8d3..7a4117f0d58a 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts @@ -42,6 +42,7 @@ import { getDetails } from '../../tasks/rule_details'; import { createSavedQueryRule, createCustomRule } from '../../tasks/api_calls/rules'; import { RULE_CREATION, SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; +import type { CompleteTimeline } from '../../objects/timeline'; const savedQueryName = 'custom saved query'; const savedQueryQuery = 'process.name: test'; @@ -56,11 +57,12 @@ describe('Custom saved_query rules', () => { beforeEach(() => { deleteAlertsAndRules(); deleteSavedQueries(); - createTimeline(getNewRule().timeline).then((response) => { + const timeline = getNewRule().timeline as CompleteTimeline; + createTimeline(timeline).then((response) => { cy.wrap({ ...getNewRule(), timeline: { - ...getNewRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts index 310efde996da..4812dcc2a626 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts @@ -6,6 +6,7 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../objects/rule'; import { ALERT_DATA_GRID, NUMBER_OF_ALERTS } from '../../screens/alerts'; @@ -59,6 +60,7 @@ import { login, visit } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; +import type { CompleteTimeline } from '../../objects/timeline'; describe('EQL rules', () => { before(() => { @@ -67,19 +69,21 @@ describe('EQL rules', () => { deleteAlertsAndRules(); }); describe('Detection rules, EQL', () => { - const expectedUrls = getEqlRule().referenceUrls.join(''); - const expectedFalsePositives = getEqlRule().falsePositivesExamples.join(''); - const expectedTags = getEqlRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getEqlRule().mitre); + const expectedUrls = getEqlRule().referenceUrls?.join(''); + const expectedFalsePositives = getEqlRule().falsePositivesExamples?.join(''); + const expectedTags = getEqlRule().tags?.join(''); + const mitreAttack = getEqlRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; const expectedNumberOfAlerts = '2 alerts'; beforeEach(() => { - createTimeline(getEqlRule().timeline).then((response) => { + const timeline = getEqlRule().timeline as CompleteTimeline; + createTimeline(timeline).then((response) => { cy.wrap({ ...getEqlRule(), timeline: { - ...getEqlRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); @@ -161,11 +165,12 @@ describe('EQL rules', () => { esArchiverLoad('auditbeat_big'); }); beforeEach(() => { - createTimeline(getEqlSequenceRule().timeline).then((response) => { + const timeline = getEqlSequenceRule().timeline as CompleteTimeline; + createTimeline(timeline).then((response) => { cy.wrap({ ...getEqlSequenceRule(), timeline: { - ...getEqlSequenceRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts index 7c3ba64536fa..4ffab681d4aa 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts @@ -6,6 +6,7 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getIndexPatterns, getNewThreatIndicatorRule, @@ -109,10 +110,11 @@ const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; describe('indicator match', () => { describe('Detection rules, Indicator Match', () => { - const expectedUrls = getNewThreatIndicatorRule().referenceUrls.join(''); - const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples.join(''); - const expectedTags = getNewThreatIndicatorRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getNewThreatIndicatorRule().mitre); + const expectedUrls = getNewThreatIndicatorRule().referenceUrls?.join(''); + const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples?.join(''); + const expectedTags = getNewThreatIndicatorRule().tags?.join(''); + const mitreAttack = getNewThreatIndicatorRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; const expectedNumberOfAlerts = '1 alert'; @@ -479,11 +481,11 @@ describe('indicator match', () => { cy.get(SCHEDULE_DETAILS).within(() => { getDetails(RUNS_EVERY_DETAILS).should( 'have.text', - `${rule.runsEvery.interval}${rule.runsEvery.type}` + `${rule.runsEvery?.interval}${rule.runsEvery?.type}` ); getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( 'have.text', - `${rule.lookBack.interval}${rule.lookBack.type}` + `${rule.lookBack?.interval}${rule.lookBack?.type}` ); }); @@ -492,7 +494,7 @@ describe('indicator match', () => { cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts); cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); - cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity.toLowerCase()); + cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.riskScore); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts index 764d8d0b688a..2c0507ca3815 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts @@ -6,6 +6,7 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getNewTermsRule, getIndexPatterns } from '../../objects/rule'; import { ALERT_DATA_GRID } from '../../screens/alerts'; @@ -60,6 +61,7 @@ import { import { login, visit } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; +import type { CompleteTimeline } from '../../objects/timeline'; describe('New Terms rules', () => { before(() => { @@ -67,19 +69,21 @@ describe('New Terms rules', () => { login(); }); describe('Detection rules, New Terms', () => { - const expectedUrls = getNewTermsRule().referenceUrls.join(''); - const expectedFalsePositives = getNewTermsRule().falsePositivesExamples.join(''); - const expectedTags = getNewTermsRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getNewTermsRule().mitre); + const expectedUrls = getNewTermsRule().referenceUrls?.join(''); + const expectedFalsePositives = getNewTermsRule().falsePositivesExamples?.join(''); + const expectedTags = getNewTermsRule().tags?.join(''); + const mitreAttack = getNewTermsRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; beforeEach(() => { + const timeline = getNewTermsRule().timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(getNewTermsRule().timeline).then((response) => { + createTimeline(timeline).then((response) => { cy.wrap({ ...getNewTermsRule(), timeline: { - ...getNewTermsRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts index 701efb2706bf..68973fdc5694 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts @@ -6,8 +6,9 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { OverrideRule } from '../../objects/rule'; +import type { Mitre, OverrideRule } from '../../objects/rule'; import { getIndexPatterns, getNewOverrideRule, getSeveritiesOverride } from '../../objects/rule'; +import type { CompleteTimeline } from '../../objects/timeline'; import { NUMBER_OF_ALERTS, ALERT_GRID_CELL } from '../../screens/alerts'; @@ -55,7 +56,7 @@ import { cleanKibana } from '../../tasks/common'; import { createAndEnableRule, fillAboutRuleWithOverrideAndContinue, - fillDefineCustomRuleWithImportedQueryAndContinue, + fillDefineCustomRuleAndContinue, fillScheduleRuleAndContinue, waitForAlertsToPopulate, waitForTheRuleToBeExecuted, @@ -66,21 +67,23 @@ import { getDetails } from '../../tasks/rule_details'; import { RULE_CREATION } from '../../urls/navigation'; describe('Detection rules, override', () => { - const expectedUrls = getNewOverrideRule().referenceUrls.join(''); - const expectedFalsePositives = getNewOverrideRule().falsePositivesExamples.join(''); - const expectedTags = getNewOverrideRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getNewOverrideRule().mitre); + const expectedUrls = getNewOverrideRule().referenceUrls?.join(''); + const expectedFalsePositives = getNewOverrideRule().falsePositivesExamples?.join(''); + const expectedTags = getNewOverrideRule().tags?.join(''); + const mitreAttack = getNewOverrideRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); before(() => { cleanKibana(); login(); }); beforeEach(() => { - createTimeline(getNewOverrideRule().timeline).then((response) => { + const timeline = getNewOverrideRule().timeline as CompleteTimeline; + createTimeline(timeline).then((response) => { cy.wrap({ ...getNewOverrideRule(), timeline: { - ...getNewOverrideRule().timeline, + ...timeline, id: response.body.data.persistTimeline.timeline.savedObjectId, }, }).as('rule'); @@ -89,7 +92,7 @@ describe('Detection rules, override', () => { it('Creates and enables a new custom rule with override option', function () { visitWithoutDateRange(RULE_CREATION); - fillDefineCustomRuleWithImportedQueryAndContinue(this.rule); + fillDefineCustomRuleAndContinue(this.rule); fillAboutRuleWithOverrideAndContinue(this.rule); fillScheduleRuleAndContinue(this.rule); createAndEnableRule(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts new file mode 100644 index 000000000000..b0f78cc76881 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getIndexConnector } from '../../objects/connector'; +import { getSimpleCustomQueryRule } from '../../objects/rule'; + +import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; +import { deleteIndex, waitForNewDocumentToBeIndexed } from '../../tasks/api_calls/elasticsearch'; +import { + cleanKibana, + deleteAlertsAndRules, + deleteConnectors, + deleteDataView, +} from '../../tasks/common'; +import { + createAndEnableRule, + fillAboutRuleAndContinue, + fillDefineCustomRuleAndContinue, + fillRuleAction, + fillScheduleRuleAndContinue, +} from '../../tasks/create_new_rule'; +import { login, visit } from '../../tasks/login'; + +import { RULE_CREATION } from '../../urls/navigation'; + +describe('Rule actions during detection rule creation', () => { + const indexConnector = getIndexConnector(); + + before(() => { + cleanKibana(); + login(); + }); + + beforeEach(() => { + deleteAlertsAndRules(); + deleteConnectors(); + deleteIndex(indexConnector.index); + deleteDataView(indexConnector.index); + }); + + const rule = { + ...getSimpleCustomQueryRule(), + actions: { throttle: 'rule', connectors: [indexConnector] }, + }; + const index = rule.actions.connectors[0].index; + const initialNumberOfDocuments = 0; + const expectedJson = JSON.parse(rule.actions.connectors[0].document); + + it('Indexes a new document after the index action is triggered ', function () { + visit(RULE_CREATION); + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + fillRuleAction(rule); + createAndEnableRule(); + goToRuleDetails(); + + /* When the rule is executed, the action is triggered. We wait for the new document to be indexed */ + waitForNewDocumentToBeIndexed(index, initialNumberOfDocuments); + + /* We assert that the new indexed document is the one set on the index action */ + cy.request({ + method: 'GET', + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }).then((response) => { + expect(response.body.hits.hits[0]._source).to.deep.equal(expectedJson); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts index 4e2b42c7b7ee..59efa1cced40 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts @@ -6,6 +6,7 @@ */ import { formatMitreAttackDescription } from '../../helpers/rules'; +import type { Mitre } from '../../objects/rule'; import { getIndexPatterns, getNewThresholdRule } from '../../objects/rule'; import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts'; @@ -60,13 +61,15 @@ import { import { login, visitWithoutDateRange } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; +import type { CompleteTimeline } from '../../objects/timeline'; describe('Detection rules, threshold', () => { let rule = getNewThresholdRule(); - const expectedUrls = getNewThresholdRule().referenceUrls.join(''); - const expectedFalsePositives = getNewThresholdRule().falsePositivesExamples.join(''); - const expectedTags = getNewThresholdRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getNewThresholdRule().mitre); + const expectedUrls = getNewThresholdRule().referenceUrls?.join(''); + const expectedFalsePositives = getNewThresholdRule().falsePositivesExamples?.join(''); + const expectedTags = getNewThresholdRule().tags?.join(''); + const mitreAttack = getNewThresholdRule().mitre as Mitre[]; + const expectedMitre = formatMitreAttackDescription(mitreAttack); before(() => { cleanKibana(); @@ -75,9 +78,10 @@ describe('Detection rules, threshold', () => { beforeEach(() => { rule = getNewThresholdRule(); + const timeline = rule.timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(getNewThresholdRule().timeline).then((response) => { - rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; + createTimeline(timeline).then((response) => { + timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); visitWithoutDateRange(RULE_CREATION); }); @@ -132,11 +136,11 @@ describe('Detection rules, threshold', () => { cy.get(SCHEDULE_DETAILS).within(() => { getDetails(RUNS_EVERY_DETAILS).should( 'have.text', - `${rule.runsEvery.interval}${rule.runsEvery.type}` + `${rule.runsEvery?.interval}${rule.runsEvery?.type}` ); getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( 'have.text', - `${rule.lookBack.interval}${rule.lookBack.type}` + `${rule.lookBack?.interval}${rule.lookBack?.type}` ); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/connector.ts b/x-pack/plugins/security_solution/cypress/objects/connector.ts index 5c2abeab0602..39c93e6f8038 100644 --- a/x-pack/plugins/security_solution/cypress/objects/connector.ts +++ b/x-pack/plugins/security_solution/cypress/objects/connector.ts @@ -5,22 +5,42 @@ * 2.0. */ -export interface EmailConnector { +interface Connector { name: string; +} + +export interface EmailConnector extends Connector { from: string; host: string; port: string; user: string; password: string; service: string; + type: 'email'; +} + +export interface IndexConnector extends Connector { + index: string; + document: string; + type: 'index'; } +export type Connectors = IndexConnector | EmailConnector; + export const getEmailConnector = (): EmailConnector => ({ - name: 'Test connector', + name: 'Test email connector', from: 'test@example.com', host: 'example.com', port: '80', user: 'username', password: 'password', service: 'Other', + type: 'email', +}); + +export const getIndexConnector = (): IndexConnector => ({ + name: 'Test index connector', + index: 'my-index-000001', + document: '{"test": "123"}', + type: 'index', }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 7b9d543e480e..245e220c340c 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -5,11 +5,13 @@ * 2.0. */ +import type { Throttle } from '@kbn/securitysolution-io-ts-alerting-types'; import { rawRules } from '../../server/lib/detection_engine/rules/prepackaged_rules'; import { getMockThreatData } from '../../public/detections/mitre/mitre_tactics_techniques'; import type { CompleteTimeline } from './timeline'; import { getTimeline, getIndicatorMatchTimelineTemplate } from './timeline'; import type { FullResponseSchema } from '../../common/detection_engine/schemas/request'; +import type { Connectors } from './connector'; export const totalNumberOfPrebuiltRules = rawRules.length; @@ -36,6 +38,11 @@ interface Interval { type: string; } +export interface Actions { + throttle: Throttle; + connectors: Connectors[]; +} + export type RuleDataSource = | { type: 'indexPatterns'; index: string[] } | { type: 'dataView'; dataView: string }; @@ -46,20 +53,21 @@ export interface CustomRule { description: string; dataSource: RuleDataSource; interval?: string; - severity: string; - riskScore: string; - tags: string[]; + severity?: string; + riskScore?: string; + tags?: string[]; timelineTemplate?: string; - referenceUrls: string[]; - falsePositivesExamples: string[]; - mitre: Mitre[]; - note: string; - runsEvery: Interval; - lookBack: Interval; - timeline: CompleteTimeline; - maxSignals: number; + referenceUrls?: string[]; + falsePositivesExamples?: string[]; + mitre?: Mitre[]; + note?: string; + runsEvery?: Interval; + lookBack?: Interval; + timeline?: CompleteTimeline; + maxSignals?: number; buildingBlockType?: string; exceptionLists?: Array<{ id: string; list_id: string; type: string; namespace_type: string }>; + actions?: Actions; } export interface ThresholdRule extends CustomRule { @@ -231,6 +239,15 @@ export const getNewRule = (): CustomRule => ({ maxSignals: 100, }); +export const getSimpleCustomQueryRule = (): CustomRule => ({ + customQuery: 'host.name: *', + dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, + name: 'New Rule Test', + description: 'The new rule description.', + runsEvery: getRunsEvery(), + lookBack: getLookBack(), +}); + export const getBuildingBlockRule = (): CustomRule => ({ customQuery: 'host.name: *', dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, @@ -489,7 +506,7 @@ export const getEditedRule = (): CustomRule => ({ ...getExistingRule(), severity: 'Medium', description: 'Edited Rule description', - tags: [...getExistingRule().tags, 'edited'], + tags: [...(getExistingRule().tags || []), 'edited'], }); export const expectedExportedRule = ( diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index e46e8a75a434..96928ff49da4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -42,6 +42,8 @@ export const EMAIL_CONNECTOR_PASSWORD_INPUT = '[data-test-subj="emailPasswordInp export const EMAIL_CONNECTOR_SERVICE_SELECTOR = '[data-test-subj="emailServiceSelectInput"]'; +export const JSON_EDITOR = "[data-test-subj='actionJsonEditor']"; + export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; @@ -58,6 +60,8 @@ export const COMBO_BOX_CLEAR_BTN = '[data-test-subj="comboBoxClearButton"]'; export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]'; +export const COMBO_BOX_SELECTION = '.euiMark'; + export const CREATE_AND_ENABLE_BTN = '[data-test-subj="create-enable"]'; export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]'; @@ -256,3 +260,7 @@ export const savedQueryByName = (savedQueryName: string) => export const APPLY_SELECTED_SAVED_QUERY_BUTTON = '[data-test-subj="saved-query-management-apply-changes-button"]'; + +export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOption']"; + +export const CREATE_CONNECTOR_BTN = "[data-test-subj='createActionConnectorButton-0']"; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/elasticsearch.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/elasticsearch.ts new file mode 100644 index 000000000000..7e2cef665403 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/elasticsearch.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const createIndex = (index: string) => { + cy.request({ + method: 'PUT', + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); +}; + +export const createDocument = (index: string, document: string) => { + cy.request({ + method: 'POST', + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_doc`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + body: JSON.parse(document), + }); +}; + +export const deleteIndex = (index: string) => { + cy.request({ + method: 'DELETE', + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); +}; + +export const waitForNewDocumentToBeIndexed = (index: string, initialNumberOfDocuments: number) => { + cy.waitUntil( + () => { + return cy + .request({ + method: 'GET', + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }) + .then((response) => { + if (response.status !== 200) { + return false; + } else { + return response.body.hits.hits.length > initialNumberOfDocuments; + } + }); + }, + { interval: 500, timeout: 12000 } + ); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 80fb77013acb..1909030b726d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -40,17 +40,21 @@ export const createCustomRule = ( rule: CustomRule, ruleId = 'rule_testing', interval = '100m' -): Cypress.Chainable> => - cy.request({ +): Cypress.Chainable> => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + const timeline = rule.timeline != null ? rule.timeline : undefined; + + return cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, interval, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'query', from: 'now-50000h', index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, @@ -60,29 +64,33 @@ export const createCustomRule = ( enabled: false, exceptions_list: rule.exceptionLists ?? [], tags: rule.tags, - ...(rule.timeline.id ?? rule.timeline.templateTimelineId + ...(timeline?.id ?? timeline?.templateTimelineId ? { - timeline_id: rule.timeline.id ?? rule.timeline.templateTimelineId, - timeline_title: rule.timeline.title, + timeline_id: timeline.id ?? timeline.templateTimelineId, + timeline_title: timeline.title, } : {}), }, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, }); +}; export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_testing') => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLowerCase() : undefined; + cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + interval: `${rule.runsEvery?.interval}${rule.runsEvery?.type}`, + from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'eql', index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, @@ -96,17 +104,20 @@ export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_test }; export const createThresholdRule = (rule: ThresholdRule, ruleId = 'rule_testing') => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + interval: `${rule.runsEvery?.interval}${rule.runsEvery?.type}`, + from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'threshold', index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, @@ -124,17 +135,20 @@ export const createThresholdRule = (rule: ThresholdRule, ruleId = 'rule_testing' }; export const createNewTermsRule = (rule: NewTermsRule, ruleId = 'rule_testing') => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, - interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`, - from: `now-${rule.lookBack.interval}${rule.lookBack.type}`, + interval: `${rule.runsEvery?.interval}${rule.runsEvery?.type}`, + from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'new_terms', index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, @@ -151,17 +165,21 @@ export const createNewTermsRule = (rule: NewTermsRule, ruleId = 'rule_testing') export const createSavedQueryRule = ( rule: SavedQueryRule, ruleId = 'saved_query_rule_testing' -): Cypress.Chainable> => - cy.request({ +): Cypress.Chainable> => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + const timeline = rule.timeline != null ? rule.timeline : undefined; + + return cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, interval: rule.interval, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'saved_query', from: 'now-50000h', index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, @@ -171,33 +189,38 @@ export const createSavedQueryRule = ( enabled: false, exceptions_list: rule.exceptionLists ?? [], tags: rule.tags, - ...(rule.timeline.id ?? rule.timeline.templateTimelineId + ...(timeline?.id ?? timeline?.templateTimelineId ? { - timeline_id: rule.timeline.id ?? rule.timeline.templateTimelineId, - timeline_title: rule.timeline.title, + timeline_id: timeline.id ?? timeline.templateTimelineId, + timeline_title: timeline.title, } : {}), }, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, }); +}; export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + const timeline = rule.timeline != null ? rule.timeline : undefined; + cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, // Default interval is 1m, our tests config overwrite this to 1s // See https://github.com/elastic/kibana/pull/125396 for details interval: '10s', name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'threat_match', - timeline_id: rule.timeline.templateTimelineId, - timeline_title: rule.timeline.title, + timeline_id: timeline?.templateTimelineId, + timeline_title: timeline?.title, threat_mapping: [ { entries: [ @@ -233,17 +256,20 @@ export const createCustomRuleEnabled = ( interval = '100m', maxSignals = 500 ) => { + const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; + const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; + if (rule.dataSource.type === 'indexPatterns') { cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, interval, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'query', from: 'now-50000h', index: rule.dataSource.index, @@ -264,11 +290,11 @@ export const createCustomRuleEnabled = ( url: 'api/detection_engine/rules', body: { rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), + risk_score: riskScore, description: rule.description, interval, name: rule.name, - severity: rule.severity.toLocaleLowerCase(), + severity, type: 'query', from: 'now-50000h', index: [], diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index cd9525e95b0b..4b6ad24c59f1 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -179,6 +179,40 @@ export const deleteCases = () => { }); }; +export const deleteConnectors = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + cy.request('POST', `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, { + query: { + bool: { + filter: [ + { + match: { + type: 'action', + }, + }, + ], + }, + }, + }); +}; + +export const deleteSavedQueries = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + cy.request('POST', `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, { + query: { + bool: { + filter: [ + { + match: { + type: 'query', + }, + }, + ], + }, + }, + }); +}; + export const postDataView = (dataSource: string) => { cy.request({ method: 'POST', @@ -196,6 +230,15 @@ export const postDataView = (dataSource: string) => { }); }; +export const deleteDataView = (dataSource: string) => { + cy.request({ + method: 'DELETE', + url: `api/data_views/data_view/${dataSource}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); +}; + export const scrollToBottom = () => cy.scrollTo('bottom'); export const waitForPageToBeLoaded = () => { diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index 2b7e8e0b3375..8f5917222071 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { EmailConnector } from '../objects/connector'; -import { getEmailConnector } from '../objects/connector'; +import type { EmailConnector, IndexConnector } from '../objects/connector'; +import { getIndexConnector, getEmailConnector } from '../objects/connector'; import type { CustomRule, MachineLearningRule, @@ -14,6 +14,7 @@ import type { ThreatIndicatorRule, ThresholdRule, NewTermsRule, + Mitre, } from '../objects/rule'; import { getMachineLearningRule } from '../objects/rule'; import { @@ -106,6 +107,14 @@ import { NEW_TERMS_HISTORY_SIZE, NEW_TERMS_HISTORY_TIME_TYPE, NEW_TERMS_INPUT_AREA, + ACTIONS_THROTTLE_INPUT, + INDEX_SELECTOR, + CREATE_CONNECTOR_BTN, + SAVE_ACTION_CONNECTOR_BTN, + JSON_EDITOR, + CREATE_ACTION_CONNECTOR_BTN, + EMAIL_ACTION_BTN, + COMBO_BOX_SELECTION, } from '../screens/create_new_rule'; import { TOAST_ERROR } from '../screens/shared'; import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; @@ -114,7 +123,6 @@ import { refreshPage } from './security_header'; import { EUI_FILTER_SELECT_ITEM } from '../screens/common/controls'; export const createAndEnableRule = () => { - cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); cy.get(CREATE_AND_ENABLE_BTN).click({ force: true }); cy.get(CREATE_AND_ENABLE_BTN).should('not.exist'); cy.get(BACK_TO_ALL_RULES_LINK).click({ force: true }); @@ -126,34 +134,41 @@ export const fillAboutRule = ( ) => { cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); + if (rule.severity) { + fillSeverity(rule.severity); + } + if (rule.riskScore) { + fillRiskScore(rule.riskScore); + } + if (rule.tags) { + fillRuleTags(rule.tags); + } + cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); - cy.get(SEVERITY_DROPDOWN).click({ force: true }); - cy.get(`#${rule.severity.toLowerCase()}`).click(); - - cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.riskScore}`, { force: true }); - - rule.tags.forEach((tag) => { - cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); - }); + if (rule.referenceUrls) { + fillReferenceUrls(rule.referenceUrls); + } - cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); + if (rule.falsePositivesExamples) { + fillFalsePositiveExamples(rule.falsePositivesExamples); + } - rule.referenceUrls.forEach((url, index) => { - cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); - cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); - }); + if (rule.mitre) { + fillMitre(rule.mitre); + } + if (rule.note) { + fillNote(rule.note); + } +}; - rule.falsePositivesExamples.forEach((falsePositive, index) => { - cy.get(FALSE_POSITIVES_INPUT) - .eq(index) - .clear({ force: true }) - .type(falsePositive, { force: true }); - cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); - }); +export const fillNote = (note: string) => { + cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(note, { force: true }); +}; +export const fillMitre = (mitreAttacks: Mitre[]) => { let techniqueIndex = 0; let subtechniqueInputIndex = 0; - rule.mitre.forEach((mitre, tacticIndex) => { + mitreAttacks.forEach((mitre, tacticIndex) => { cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).eq(tacticIndex).click({ force: true }); cy.contains(MITRE_TACTIC, mitre.tactic).click(); @@ -175,8 +190,38 @@ export const fillAboutRule = ( cy.get(MITRE_ATTACK_ADD_TACTIC_BUTTON).click({ force: true }); }); +}; - cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(rule.note, { force: true }); +export const fillFalsePositiveExamples = (falsePositives: string[]) => { + falsePositives.forEach((falsePositive, index) => { + cy.get(FALSE_POSITIVES_INPUT) + .eq(index) + .clear({ force: true }) + .type(falsePositive, { force: true }); + cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); + }); +}; + +export const fillSeverity = (severity: string) => { + cy.get(SEVERITY_DROPDOWN).click({ force: true }); + cy.get(`#${severity.toLowerCase()}`).click(); +}; + +export const fillRiskScore = (riskScore: string) => { + cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${riskScore}`, { force: true }); +}; + +export const fillRuleTags = (tags: string[]) => { + tags.forEach((tag) => { + cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); + }); +}; + +export const fillReferenceUrls = (referenceUrls: string[]) => { + referenceUrls.forEach((url, index) => { + cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); + cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); + }); }; export const fillAboutRuleAndContinue = ( @@ -200,8 +245,9 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => { }); }); - cy.get(SEVERITY_DROPDOWN).click({ force: true }); - cy.get(`#${rule.severity.toLowerCase()}`).click(); + if (rule.severity) { + fillSeverity(rule.severity); + } cy.get(RISK_MAPPING_OVERRIDE_OPTION).click(); cy.get(RISK_OVERRIDE).within(() => { @@ -210,48 +256,24 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => { cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.riskScore}`, { force: true }); - rule.tags.forEach((tag) => { - cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); - }); + if (rule.tags) { + fillRuleTags(rule.tags); + } cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); - rule.referenceUrls.forEach((url, index) => { - cy.get(REFERENCE_URLS_INPUT).eq(index).type(url, { force: true }); - cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); - }); - - rule.falsePositivesExamples.forEach((falsePositive, index) => { - cy.get(FALSE_POSITIVES_INPUT).eq(index).type(falsePositive, { force: true }); - cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); - }); - - let techniqueIndex = 0; - let subtechniqueInputIndex = 0; - rule.mitre.forEach((mitre, tacticIndex) => { - cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).eq(tacticIndex).click({ force: true }); - cy.contains(MITRE_TACTIC, mitre.tactic).click(); - - mitre.techniques.forEach((technique) => { - cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).eq(tacticIndex).click({ force: true }); - cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).eq(techniqueIndex).click({ force: true }); - cy.contains(MITRE_TACTIC, technique.name).click(); - - technique.subtechniques.forEach((subtechnique) => { - cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).eq(techniqueIndex).click({ force: true }); - cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN) - .eq(subtechniqueInputIndex) - .click({ force: true }); - cy.contains(MITRE_TACTIC, subtechnique).click(); - subtechniqueInputIndex++; - }); - techniqueIndex++; - }); - - cy.get(MITRE_ATTACK_ADD_TACTIC_BUTTON).click({ force: true }); - }); - - cy.get(INVESTIGATION_NOTES_TEXTAREA).type(rule.note, { force: true }); + if (rule.referenceUrls) { + fillReferenceUrls(rule.referenceUrls); + } + if (rule.falsePositivesExamples) { + fillFalsePositiveExamples(rule.falsePositivesExamples); + } + if (rule.mitre) { + fillMitre(rule.mitre); + } + if (rule.note) { + fillNote(rule.note); + } cy.get(RULE_NAME_OVERRIDE).within(() => { cy.get(COMBO_BOX_INPUT).type(`${rule.nameOverride}{enter}`); @@ -264,35 +286,65 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => { getAboutContinueButton().should('exist').click({ force: true }); }; -export const fillDefineCustomRuleWithImportedQueryAndContinue = ( - rule: CustomRule | OverrideRule -) => { +export const fillCustomQuery = (rule: CustomRule | OverrideRule) => { + if (rule.timeline?.id) { + cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); + cy.get(TIMELINE(rule.timeline.id)).click(); + cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); + } else { + cy.get(CUSTOM_QUERY_INPUT) + .first() + .type(rule.customQuery || ''); + } +}; + +export const fillDefineCustomRuleAndContinue = (rule: CustomRule | OverrideRule) => { if (rule.dataSource.type === 'dataView') { cy.get(DATA_VIEW_OPTION).click(); cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.dataSource.dataView}{enter}`); } - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id)).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); - + fillCustomQuery(rule); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); - cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRule) => { - cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(rule.runsEvery.interval); - cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType); - cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(rule.lookBack.interval); - cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType); + if (rule.runsEvery) { + cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(rule.runsEvery.interval); + cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType); + } + if (rule.lookBack) { + cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(rule.lookBack.interval); + cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType); + } + cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); +}; + +export const fillRuleAction = (rule: CustomRule) => { + if (rule.actions) { + cy.get(ACTIONS_THROTTLE_INPUT).select(rule.actions.throttle); + rule.actions?.connectors.forEach((connector) => { + switch (connector.type) { + case 'index': + cy.get(INDEX_SELECTOR).click(); + cy.get(CREATE_CONNECTOR_BTN).click(); + fillIndexConnectorForm(connector); + break; + case 'email': + cy.get(EMAIL_ACTION_BTN).click(); + cy.get(CREATE_ACTION_CONNECTOR_BTN).click(); + fillEmailConnectorForm(connector); + break; + } + }); + } }; export const fillDefineThresholdRule = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id)).click(); + fillCustomQuery(rule); cy.get(COMBO_BOX_CLEAR_BTN).first().click(); if (rule.dataSource.type === 'indexPatterns') { @@ -318,9 +370,7 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const typeThresholdField = ($el: Cypress.ObjectLike) => cy.wrap($el).type(rule.thresholdField, { delay: 35 }); - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id)).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); + fillCustomQuery(rule); cy.get(THRESHOLD_INPUT_AREA) .find(INPUT) .then((inputs) => { @@ -360,9 +410,7 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { }; export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRule) => { - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id)).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); + fillCustomQuery(rule); cy.get(NEW_TERMS_INPUT_AREA).find(INPUT).click().type(rule.newTermsFields[0], { delay: 35 }); cy.get(EUI_FILTER_SELECT_ITEM).click({ force: true }); cy.focused().type('{esc}'); // Close combobox dropdown so next inputs can be interacted with @@ -449,6 +497,21 @@ export const fillEmailConnectorForm = (connector: EmailConnector = getEmailConne cy.get(EMAIL_CONNECTOR_PASSWORD_INPUT).type(connector.password); }; +export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConnector()) => { + cy.get(CONNECTOR_NAME_INPUT).type(connector.name); + cy.get(COMBO_BOX_INPUT).type(connector.index); + + cy.get(COMBO_BOX_SELECTION).click({ force: true }); + + cy.get(SAVE_ACTION_CONNECTOR_BTN).click(); + cy.get(SAVE_ACTION_CONNECTOR_BTN).should('not.exist'); + cy.get(JSON_EDITOR).should('be.visible'); + cy.get(JSON_EDITOR).click(); + cy.get(JSON_EDITOR).type(connector.document, { + parseSpecialCharSequences: false, + }); +}; + /** Returns the indicator index drop down field. Pass in row number, default is 1 */ export const getIndicatorIndexComboField = (row = 1) => cy.get(THREAT_COMBO_BOX_INPUT).eq(row * 2 - 2); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx index 135d4783da82..643e1b69a513 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx @@ -148,6 +148,7 @@ export const JsonEditorWithMessageVariables: React.FunctionComponent = ({ return ( 0 && inputTargetValue !== undefined} From 3811568a7ee6e720a32fe389357e55105784d0cc Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:05:55 -0600 Subject: [PATCH 118/185] skip failing test suite (#142152) --- .../apps/ml/data_frame_analytics/results_view_content.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts index 2bddf0a7d951..af023ea52d9b 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts @@ -14,7 +14,8 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - describe('results view content and total feature importance', function () { + // Failing: See https://github.com/elastic/kibana/issues/142152 + describe.skip('results view content and total feature importance', function () { const testDataList: Array<{ suiteTitle: string; archive: string; From b94538185a184309de5186432fdb07be38ba3da1 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 29 Sep 2022 16:24:08 +0200 Subject: [PATCH 119/185] Fix needless tool tip inside host and user risk inspect modal (#142219) --- .../cti_details/host_risk_summary.tsx | 1 - .../cti_details/user_risk_summary.tsx | 1 - .../risk_score_deprecated/index.tsx | 11 +++++++- .../risk_score_header_title.tsx | 23 ----------------- .../risk_score_no_data_detected.tsx | 11 +++++++- .../host_risk_score_table/index.tsx | 25 +++---------------- .../host_risk_score/index.tsx | 2 ++ .../user_risk_score/index.tsx | 2 ++ .../components/host_overview/index.tsx | 2 -- .../components/user_overview/index.tsx | 2 -- .../user_risk_score_table/index.tsx | 25 +++---------------- 11 files changed, 30 insertions(+), 75 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx index 68d2d9a65157..c4380550c422 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx @@ -31,7 +31,6 @@ const HostRiskSummaryComponent: React.FC<{ } toolTipContent={ diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx index c97878cf6f5e..5cda8c903dd1 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx @@ -31,7 +31,6 @@ const UserRiskSummaryComponent: React.FC<{ } toolTipContent={ diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_deprecated/index.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_deprecated/index.tsx index 13ec1df29ae0..f4391f13fd21 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_deprecated/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_deprecated/index.tsx @@ -13,6 +13,7 @@ import { useCheckSignalIndex } from '../../../../detections/containers/detection import type { inputsModel } from '../../../store'; import { HeaderSection } from '../../header_section'; import * as i18n from './translations'; +import * as overviewI18n from '../../../../overview/components/entity_analytics/common/translations'; import { RiskScoreHeaderTitle } from '../risk_score_onboarding/risk_score_header_title'; export const RiskScoresDeprecated = ({ @@ -46,7 +47,15 @@ export const RiskScoresDeprecated = ({ return ( - } titleSize="s" /> + } + titleSize="s" + tooltip={ + entityType === RiskScoreEntity.user + ? overviewI18n.USER_RISK_TABLE_TOOLTIP + : overviewI18n.HOST_RISK_TABLE_TOOLTIP + } + /> {translations.cta}} body={translations.body} diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx index dffb9c646d7c..47eb0d9cb61d 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx @@ -5,46 +5,23 @@ * 2.0. */ import React from 'react'; -import styled from 'styled-components'; -import { EuiIconTip } from '@elastic/eui'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { NavItemBetaBadge } from '../../navigation/nav_item_beta_badge'; import * as i18n from '../../../../overview/components/entity_analytics/common/translations'; import { TECHNICAL_PREVIEW } from './translations'; -const IconWrapper = styled.span` - margin-left: ${({ theme }) => theme.eui.euiSizeS}; -`; - const RiskScoreHeaderTitleComponent = ({ riskScoreEntity, - showTooltip = true, title, }: { riskScoreEntity: RiskScoreEntity; - showTooltip?: boolean; title?: string; }) => { return ( <> {title ?? (riskScoreEntity === RiskScoreEntity.user ? i18n.USER_RISK_TITLE : i18n.HOST_RISK_TITLE)} - {showTooltip && ( - - - - )} ); diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx index b492ee51e42a..1baafecd663d 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx @@ -14,6 +14,7 @@ import * as i18n from './translations'; import { RiskScoreHeaderTitle } from './risk_score_header_title'; import { RiskScoreRestartButton } from './risk_score_restart_button'; import type { inputsModel } from '../../../store'; +import * as overviewI18n from '../../../../overview/components/entity_analytics/common/translations'; const RiskScoresNoDataDetectedComponent = ({ entityType, @@ -33,7 +34,15 @@ const RiskScoresNoDataDetectedComponent = ({ return ( - } titleSize="s" /> + } + titleSize="s" + tooltip={ + entityType === RiskScoreEntity.user + ? overviewI18n.USER_RISK_TABLE_TOOLTIP + : overviewI18n.HOST_RISK_TABLE_TOOLTIP + } + /> {translations.title}} body={translations.body} diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx index 9a2138786b3a..dd2aacef77f6 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx @@ -8,8 +8,7 @@ import React, { useMemo, useCallback } from 'react'; import { useDispatch } from 'react-redux'; -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; -import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { Columns, Criteria, ItemsPerRow } from '../../../common/components/paginated_table'; import { PaginatedTable } from '../../../common/components/paginated_table'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; @@ -43,10 +42,6 @@ export const rowItems: ItemsPerRow[] = [ }, ]; -const IconWrapper = styled.span` - margin-left: ${({ theme }) => theme.eui.euiSizeS}; -`; - const tableType = hostsModel.HostsTableType.risk; interface HostRiskScoreTableProps { @@ -150,21 +145,6 @@ const HostRiskScoreTableComponent: React.FC = ({ ); - const headerTitle = ( - <> - {i18nHosts.HOST_RISK_TITLE} - - - - - ); - const getHostRiskScoreFilterQuerySelector = useMemo( () => hostsSelectors.hostRiskScoreSeverityFilterSelector(), [] @@ -200,8 +180,9 @@ const HostRiskScoreTableComponent: React.FC = ({ /> } headerSupplement={risk} - headerTitle={headerTitle} + headerTitle={i18nHosts.HOST_RISK_TITLE} headerUnit={i18n.UNIT(totalCount)} + headerTooltip={i18nHosts.HOST_RISK_TABLE_TOOLTIP} id={id} isInspect={isInspect} itemsPerRow={rowItems} diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx index 49df0b1b0e45..9bb06fd261dd 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx @@ -37,6 +37,7 @@ import { RiskScoresNoDataDetected } from '../../../../common/components/risk_sco import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries'; import { Loader } from '../../../../common/components/loader'; import { Panel } from '../../../../common/components/panel'; +import * as commonI18n from '../common/translations'; const TABLE_QUERY_ID = 'hostRiskDashboardTable'; const HOST_RISK_KPI_QUERY_ID = 'headerHostRiskScoreKpiQuery'; @@ -161,6 +162,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { id={TABLE_QUERY_ID} toggleStatus={toggleStatus} toggleQuery={setToggleStatus} + tooltip={commonI18n.HOST_RISK_TABLE_TOOLTIP} > {toggleStatus && ( diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx index 034e62bb37ad..cf01417d0eab 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx @@ -37,6 +37,7 @@ import { RiskScoresNoDataDetected } from '../../../../common/components/risk_sco import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries'; import { Loader } from '../../../../common/components/loader'; import { Panel } from '../../../../common/components/panel'; +import * as commonI18n from '../common/translations'; const TABLE_QUERY_ID = 'userRiskDashboardTable'; const USER_RISK_KPI_QUERY_ID = 'headerUserRiskScoreKpiQuery'; @@ -161,6 +162,7 @@ const EntityAnalyticsUserRiskScoresComponent = () => { id={TABLE_QUERY_ID} toggleStatus={toggleStatus} toggleQuery={setToggleStatus} + tooltip={commonI18n.USER_RISK_TABLE_TOOLTIP} > {toggleStatus && ( diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index 70c304e03116..fe221ee9b098 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -119,7 +119,6 @@ export const HostOverview = React.memo( ), description: ( @@ -135,7 +134,6 @@ export const HostOverview = React.memo( ), description: ( diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx index 68b40f581d38..8cd4ee428aea 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx @@ -119,7 +119,6 @@ export const UserOverview = React.memo( ), description: ( @@ -135,7 +134,6 @@ export const UserOverview = React.memo( ), description: ( diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx index 245150f4fb49..984f737d9f97 100644 --- a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx @@ -8,8 +8,7 @@ import React, { useMemo, useCallback } from 'react'; import { useDispatch } from 'react-redux'; -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; -import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { Columns, Criteria, ItemsPerRow } from '../../../common/components/paginated_table'; import { PaginatedTable } from '../../../common/components/paginated_table'; @@ -32,10 +31,6 @@ import type { UserRiskScore, } from '../../../../common/search_strategy'; -const IconWrapper = styled.span` - margin-left: ${({ theme }) => theme.eui.euiSizeS}; -`; - export const rowItems: ItemsPerRow[] = [ { text: i18n.ROWS_5, @@ -152,21 +147,6 @@ const UserRiskScoreTableComponent: React.FC = ({ ); - const headerTitle = ( - <> - {i18nUsers.NAVIGATION_RISK_TITLE} - - - - - ); - const getUserRiskScoreFilterQuerySelector = useMemo( () => usersSelectors.userRiskScoreSeverityFilterSelector(), [] @@ -201,7 +181,8 @@ const UserRiskScoreTableComponent: React.FC = ({ /> } headerSupplement={risk} - headerTitle={headerTitle} + headerTitle={i18nUsers.NAVIGATION_RISK_TITLE} + headerTooltip={i18n.USER_RISK_TABLE_TOOLTIP} headerUnit={i18n.UNIT(totalCount)} id={id} isInspect={isInspect} From 9993b56233adb51e7108d64fb6c27b51394b045c Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:27:28 +0200 Subject: [PATCH 120/185] [Fleet] added last checkin message to Agent UI (#142220) * added last checkin message to Agent UI * added last_checkin_message to FleetServerAgent --- .../fleet/common/types/models/agent.ts | 5 ++ .../agent_details/agent_details_overview.tsx | 6 +++ .../agents/components/agent_health.tsx | 47 ++++++++++++------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 5def12287b4f..fa54f8c943e2 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -84,6 +84,7 @@ interface AgentBase { policy_revision?: number | null; last_checkin?: string; last_checkin_status?: 'error' | 'online' | 'degraded' | 'updating'; + last_checkin_message?: string; user_provided_metadata: AgentMetadata; local_metadata: AgentMetadata; tags?: string[]; @@ -229,6 +230,10 @@ export interface FleetServerAgent { * Last checkin status */ last_checkin_status?: 'error' | 'online' | 'degraded' | 'updating'; + /** + * Last checkin message + */ + last_checkin_message?: string; /** * ID of the API key the Elastic Agent uses to authenticate with elasticsearch */ 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 5799b130b6a4..11ddcef4c8ae 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 @@ -58,6 +58,12 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ '-' ), }, + { + title: i18n.translate('xpack.fleet.agentDetails.lastCheckinMessageLabel', { + defaultMessage: 'Last checkin message', + }), + description: agent.last_checkin_message ? agent.last_checkin_message : '-', + }, { title: i18n.translate('xpack.fleet.agentDetails.hostIdLabel', { defaultMessage: 'Agent ID', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx index b2c03ba745d7..93091ff72625 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx @@ -74,8 +74,33 @@ export const AgentHealth: React.FunctionComponent = ({ agent, showOfflinePreviousStatus, }) => { - const { last_checkin: lastCheckIn } = agent; + const { last_checkin: lastCheckIn, last_checkin_message: lastCheckInMessage } = agent; const msLastCheckIn = new Date(lastCheckIn || 0).getTime(); + const lastCheckInMessageText = lastCheckInMessage ? ( + + ) : null; + const lastCheckinText = msLastCheckIn ? ( + <> + , + }} + /> + + ) : ( + + ); const previousToOfflineStatus = useMemo(() => { if (!showOfflinePreviousStatus || agent.status !== 'offline') { @@ -89,22 +114,10 @@ export const AgentHealth: React.FunctionComponent = ({ - , - }} - /> - - ) : ( - - ) + <> +

    {lastCheckinText}

    +

    {lastCheckInMessageText}

    + } > <> From 9ed75bfedaded5b07e3c062677937b6cdc2bfdc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 23:57:44 +0930 Subject: [PATCH 121/185] Update dependency @elastic/charts to v50 (main) (#141252) * Update dependency @elastic/charts to v50 * update test screenshot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Marco Vettorello --- package.json | 2 +- .../charts/__snapshots__/donut_chart.test.tsx.snap | 11 +++++++++++ yarn.lock | 8 ++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3bbcabba0cdd..75751ce826df 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@dnd-kit/utilities": "^2.0.0", "@elastic/apm-rum": "^5.12.0", "@elastic/apm-rum-react": "^1.4.2", - "@elastic/charts": "49.0.0", + "@elastic/charts": "50.0.1", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.3.0-canary.1", "@elastic/ems-client": "8.3.3", diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap index b4486e65c539..9989a4d5df13 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap @@ -233,6 +233,17 @@ exports[`DonutChart component passes correct props without errors for valid prop "visible": true, }, }, + "flamegraph": Object { + "navigation": Object { + "buttonBackgroundColor": "rgb(204, 228, 245)", + "buttonDisabledBackgroundColor": "rgba(211, 218, 230, 0.15)", + "buttonDisabledTextColor": "rgb(162, 171, 186)", + "buttonTextColor": "rgb(0, 97, 166)", + "textColor": "rgb(52, 55, 65)", + }, + "scrollbarThumb": "rgb(52, 55, 65)", + "scrollbarTrack": "rgb(211, 218, 230)", + }, "goal": Object { "arcBoxSamplePitch": 0.08726646259971647, "barThicknessMinSizeRatio": 0.1, diff --git a/yarn.lock b/yarn.lock index 804502ebeb4c..2902890199d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1455,10 +1455,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@49.0.0": - version "49.0.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-49.0.0.tgz#6cda2bdb8c92691c12843ba44014fad5a60d61b0" - integrity sha512-LXurKWGyPWXgMUJ6l21tSI4y9N3lJh3yJkDGCjvW9M5P3CXICR1V9ZizMMZlj9p1OXULsccrInsiUBFMU0Ktog== +"@elastic/charts@50.0.1": + version "50.0.1" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-50.0.1.tgz#a0ee66668c857be7cfea2e134e0b84930d1564c5" + integrity sha512-O1L8rot0dycTQo/0eW7aD2P4K3Bh1LtzMgRBXYZAtIpbzdxveRkl8fOlIkGxeeHE4YNvntUJaJWeyT+ngGg7QA== dependencies: "@popperjs/core" "^2.4.0" bezier-easing "^2.1.0" From 5a5a1a98aaf210a94b3b0b6053be626059347674 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Thu, 29 Sep 2022 16:30:25 +0200 Subject: [PATCH 122/185] [Security Solution] Fix flaky e2e tests for Related Integrations (#142134) **Related to:** https://github.com/elastic/kibana/pull/142040, https://github.com/elastic/kibana/pull/135181 ## Summary - Fixes flakiness in Cypress tests for the "related integrations" feature. - Unskips tests that have been skipped in https://github.com/elastic/kibana/pull/142040 --- .../related_integrations.cy.ts | 94 +++++++++++-------- .../cypress/tasks/alerts_detection_rules.ts | 30 +++++- .../cypress/tasks/api_calls/fleet.ts | 80 ++++++++++++++++ .../security_solution/cypress/tasks/common.ts | 42 --------- .../cypress/tasks/integrations.ts | 4 +- 5 files changed, 168 insertions(+), 82 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/tasks/api_calls/fleet.ts diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts index 02ccff0c265d..f6a80a8a15f4 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/related_integrations.cy.ts @@ -5,47 +5,52 @@ * 2.0. */ +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; + +import { FIELD } from '../../screens/alerts_details'; +import { INTEGRATIONS, INTEGRATIONS_STATUS } from '../../screens/rule_details'; import { INTEGRATIONS_POPOVER, INTEGRATIONS_POPOVER_TITLE, RULE_NAME, } from '../../screens/alerts_detection_rules'; -import { INTEGRATIONS, INTEGRATIONS_STATUS } from '../../screens/rule_details'; + +import { cleanFleet } from '../../tasks/api_calls/fleet'; +import { importRule } from '../../tasks/api_calls/rules'; +import { + disableRelatedIntegrations, + enableRelatedIntegrations, +} from '../../tasks/api_calls/kibana_advanced_settings'; + +import { cleanKibana } from '../../tasks/common'; +import { login, visit } from '../../tasks/login'; +import { expandFirstAlert } from '../../tasks/alerts'; +import { filterBy, openTable } from '../../tasks/alerts_details'; +import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; +import { installAwsCloudFrontWithPolicy } from '../../tasks/integrations'; import { enableRule, goToTheRuleDetailsOf, openIntegrationsPopover, + waitForRulesTableToShow, waitForRuleToChangeStatus, } from '../../tasks/alerts_detection_rules'; -import { importRule } from '../../tasks/api_calls/rules'; -import { cleanKibana, cleanPackages } from '../../tasks/common'; -import { login, visit } from '../../tasks/login'; -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { installAwsCloudFrontWithPolicy } from '../../tasks/integrations'; -import { expandFirstAlert } from '../../tasks/alerts'; -import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; -import { filterBy, openTable } from '../../tasks/alerts_details'; -import { FIELD } from '../../screens/alerts_details'; -import { - disableRelatedIntegrations, - enableRelatedIntegrations, -} from '../../tasks/api_calls/kibana_advanced_settings'; /* - Note that the rule we are using for testing purposes has the following characteristics, changing that may affect the coverage. - - - Single-integration - - Package: system - - Multi-integration package - - Package: aws - - Integration: cloudtrail - - Integration: cloudfront - - Not existing package: - - Package: unknown - - Not existing integration & existing package: - - Package: aws - - Integration: unknown - */ +Note that the rule we are using for testing purposes has the following characteristics, changing that may affect the coverage. + +- Single-integration + - Package: system +- Multi-integration package + - Package: aws + - Integration: cloudtrail + - Integration: cloudfront +- Not existing package: + - Package: unknown +- Not existing integration & existing package: + - Package: aws + - Integration: unknown +*/ describe('Related integrations', () => { before(() => { @@ -62,8 +67,12 @@ describe('Related integrations', () => { }; before(() => { - cleanPackages(); + cleanFleet(); + }); + + beforeEach(() => { visit(DETECTIONS_RULE_MANAGEMENT_URL); + waitForRulesTableToShow(); }); it('should display a badge with the installed integrations on the rule management page', () => { @@ -117,19 +126,24 @@ describe('Related integrations', () => { }; before(() => { - cleanPackages(); - installAwsCloudFrontWithPolicy(); + cleanFleet().then(() => { + installAwsCloudFrontWithPolicy(); + }); + }); + + beforeEach(() => { visit(DETECTIONS_RULE_MANAGEMENT_URL); + waitForRulesTableToShow(); }); - it.skip('should display a badge with the installed integrations on the rule management page', () => { + it('should display a badge with the installed integrations on the rule management page', () => { cy.get(INTEGRATIONS_POPOVER).should( 'have.text', `${rule.enabledIntegrations}/${rule.integrations.length} integrations` ); }); - it.skip('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { + it('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { openIntegrationsPopover(); cy.get(INTEGRATIONS_POPOVER_TITLE).should( @@ -148,7 +162,7 @@ describe('Related integrations', () => { }); }); - it.skip('should display the integrations on the definition section', () => { + it('should display the integrations on the definition section', () => { goToTheRuleDetailsOf(rule.name); cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); @@ -169,7 +183,6 @@ describe('Related integrations', () => { const expectedRelatedIntegrationsText = '{"package":"system","version":"1.17.0"}{"package":"aws","integration":"cloudtrail","version":"1.17.0"}{"package":"aws","integration":"cloudfront","version":"1.17.0"}{"package":"aws","integration":"unknown","version":"1.17.0"}'; - visit(DETECTIONS_RULE_MANAGEMENT_URL); enableRule(firstRule); waitForRuleToChangeStatus(); goToTheRuleDetailsOf(rule.name); @@ -193,15 +206,20 @@ describe('Related integrations', () => { }; before(() => { - cleanPackages(); - disableRelatedIntegrations(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); + cleanFleet().then(() => { + disableRelatedIntegrations(); + }); }); after(() => { enableRelatedIntegrations(); }); + beforeEach(() => { + visit(DETECTIONS_RULE_MANAGEMENT_URL); + waitForRulesTableToShow(); + }); + it('should not display a badge with the installed integrations on the rule management page', () => { cy.get(RULE_NAME).should('have.text', rule.name); cy.get(INTEGRATIONS).should('not.exist'); diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 4432059c7f20..b8e38d3e11e0 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -253,9 +253,37 @@ export const sortByEnabledRules = () => { cy.get(SORT_RULES_BTN).contains('Enabled').click({ force: true }); }; +/** + * Because the Rule Management page is relatively slow, in order to avoid timeouts and flakiness, + * we almost always want to wait until the Rules table is "loaded" before we do anything with it. + * + * This task should be sufficient for the vast majority of the tests. It waits for the table + * to show up on the page, but doesn't wait until it is fully loaded and filled with rows. + * + * @example + * beforeEach(() => { + * visit(DETECTIONS_RULE_MANAGEMENT_URL); // returns on "load" event, still lots of work to do + * waitForRulesTableToShow(); // a lot has done in React and the table shows up on the page + * }); + */ +export const waitForRulesTableToShow = () => { + // Wait up to 5 minutes for the table to show up as in CI containers this can be very slow + cy.get(RULES_TABLE, { timeout: 300000 }).should('exist'); +}; + +/** + * Because the Rule Management page is relatively slow, in order to avoid timeouts and flakiness, + * we almost always want to wait until the Rules table is "loaded" before we do anything with it. + * + * This task can be needed for some tests that e.g. check the table load/refetch/pagination logic. + * It waits for the table's own loading indicator to show up and disappear. + * + * NOTE: Normally, we should not rely on loading indicators in tests, because due to their + * dynamic nature it's possible to introduce race conditions and flakiness. + */ export const waitForRulesTableToBeLoaded = () => { - cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR).should('exist'); // Wait up to 5 minutes for the rules to load as in CI containers this can be very slow + cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR, { timeout: 300000 }).should('exist'); cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR, { timeout: 300000 }).should('not.exist'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/fleet.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/fleet.ts new file mode 100644 index 000000000000..da5ab6ba488d --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/fleet.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Deletes all existing Fleet packages, package policies and agent policies. + */ +export const cleanFleet = () => { + // NOTE: order does matter. + return deletePackagePolicies() + .then(() => { + deletePackages(); + }) + .then(() => { + deleteAgentPolicies(); + }); +}; + +const deleteAgentPolicies = () => { + return cy + .request({ + method: 'GET', + url: 'api/fleet/agent_policies', + headers: { 'kbn-xsrf': 'cypress-creds' }, + }) + .then((response) => { + response.body.items.forEach((item: { id: string }) => { + cy.request({ + method: 'POST', + url: `api/fleet/agent_policies/delete`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + body: { + agentPolicyId: item.id, + }, + }); + }); + }); +}; + +const deletePackagePolicies = () => { + return cy + .request({ + method: 'GET', + url: 'api/fleet/package_policies', + headers: { 'kbn-xsrf': 'cypress-creds' }, + }) + .then((response) => { + cy.request({ + method: 'POST', + url: `api/fleet/package_policies/delete`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + body: { + packagePolicyIds: response.body.items.map((item: { id: string }) => item.id), + }, + }); + }); +}; + +const deletePackages = () => { + return cy + .request({ + method: 'GET', + url: 'api/fleet/epm/packages', + headers: { 'kbn-xsrf': 'cypress-creds' }, + }) + .then((response) => { + response.body.items.forEach((item: { status: string; name: string; version: string }) => { + if (item.status === 'installed') { + cy.request({ + method: 'DELETE', + url: `api/fleet/epm/packages/${item.name}/${item.version}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); + } + }); + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index 4b6ad24c59f1..e576c25cc256 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -62,48 +62,6 @@ export const cleanKibana = () => { deleteTimelines(); }; -export const cleanPackages = () => { - deletePolicies(); - deletePackages(); -}; - -export const deletePolicies = () => { - cy.request({ - method: 'GET', - url: 'api/fleet/agent_policies', - headers: { 'kbn-xsrf': 'cypress-creds' }, - }).then((response) => { - response.body.items.forEach((item: { id: string }) => { - cy.request({ - method: 'POST', - url: `api/fleet/agent_policies/delete`, - headers: { 'kbn-xsrf': 'cypress-creds' }, - body: { - agentPolicyId: item.id, - }, - }); - }); - }); -}; - -export const deletePackages = () => { - cy.request({ - method: 'GET', - url: 'api/fleet/epm/packages', - headers: { 'kbn-xsrf': 'cypress-creds' }, - }).then((response) => { - response.body.items.forEach((item: { status: string; name: string; version: string }) => { - if (item.status === 'installed') { - cy.request({ - method: 'DELETE', - url: `api/fleet/epm/packages/${item.name}/${item.version}`, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); - } - }); - }); -}; - export const deleteAlertsAndRules = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; diff --git a/x-pack/plugins/security_solution/cypress/tasks/integrations.ts b/x-pack/plugins/security_solution/cypress/tasks/integrations.ts index 1da83a591aa1..0150d0c83c3d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/integrations.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/integrations.ts @@ -18,6 +18,8 @@ export const installAwsCloudFrontWithPolicy = () => { visit('app/integrations/detail/aws-1.17.0/overview?integration=cloudfront'); cy.get(ADD_INTEGRATION_BTN).click(); cy.get(QUEUE_URL).type('http://www.example.com'); + + // Fleet installs an integration very slowly, so we have to increase the timeout here. cy.get(SAVE_AND_CONTINUE_BTN).click(); - cy.get(INTEGRATION_ADDED_POP_UP).should('exist'); + cy.get(INTEGRATION_ADDED_POP_UP, { timeout: 120000 }).should('exist'); }; From c56d738d6fda2ba84fa1f6b0dcf8568664995018 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 29 Sep 2022 17:01:30 +0200 Subject: [PATCH 123/185] Fix Host risk classification filter sort order changes when option is clicked (#142207) --- .../security_solution/risk_score/all/index.ts | 8 ++++ .../severity/severity_filter_group.test.tsx | 42 +++++++++++++++++++ .../severity/severity_filter_group.tsx | 9 ++-- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.test.tsx diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts index 5a773d49134d..cefd43fe5f99 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -105,3 +105,11 @@ export const EMPTY_SEVERITY_COUNT = { [RiskSeverity.moderate]: 0, [RiskSeverity.unknown]: 0, }; + +export const SEVERITY_UI_SORT_ORDER = [ + RiskSeverity.unknown, + RiskSeverity.low, + RiskSeverity.moderate, + RiskSeverity.high, + RiskSeverity.critical, +]; diff --git a/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.test.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.test.tsx new file mode 100644 index 000000000000..187f743f0b46 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { SeverityFilterGroup } from './severity_filter_group'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { TestProviders } from '../../mock'; + +describe('SeverityFilterGroup', () => { + it('preserves sort order when severityCount is out of order', () => { + const { getByTestId, getAllByTestId } = render( + + + + ); + + fireEvent.click(getByTestId('risk-filter-button')); + + expect(getAllByTestId('risk-score').map((ele) => ele.textContent)).toEqual([ + 'Unknown', + 'Low', + 'Moderate', + 'High', + 'Critical', + ]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx index 126f3e3870ab..ea05c31d6f48 100644 --- a/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import type { RiskSeverity } from '../../../../common/search_strategy'; +import { SEVERITY_UI_SORT_ORDER } from '../../../../common/search_strategy'; import type { SeverityCount } from './types'; import { RiskScore } from './common'; @@ -46,10 +47,10 @@ export const SeverityFilterGroup: React.FC<{ const items: SeverityItems[] = useMemo(() => { const checked: FilterChecked = 'on'; - return (Object.keys(severityCount) as RiskSeverity[]).map((k) => ({ - risk: k, - count: severityCount[k], - checked: selectedSeverities.includes(k) ? checked : undefined, + return SEVERITY_UI_SORT_ORDER.map((severity) => ({ + risk: severity, + count: severityCount[severity], + checked: selectedSeverities.includes(severity) ? checked : undefined, })); }, [severityCount, selectedSeverities]); From b0432083a61c5df9a8695c6308e11e90a228ed01 Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Thu, 29 Sep 2022 10:10:28 -0500 Subject: [PATCH 124/185] [Security Solution] use endpoint rbac for process operations (#142031) --- .../endpoint/service/authz/authz.test.ts | 49 ++++++++----------- .../common/endpoint/service/authz/authz.ts | 13 +++-- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts index b9c0dcff2054..d8f65cf07d28 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts @@ -108,37 +108,28 @@ describe('Endpoint Authz service', () => { }); }); - describe('endpoint rbac is enabled', () => { - describe('canIsolateHost', () => { - it('should be true if packagePrivilege.writeHostIsolation is true', () => { - fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = - true; - const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); - expect(authz.canIsolateHost).toBe(true); - }); - - it('should be false if packagePrivilege.writeHostIsolation is false', () => { - fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = - false; - const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); - expect(authz.canIsolateHost).toBe(false); - }); + describe('and endpoint rbac is enabled', () => { + it.each<[EndpointAuthzKeyList[number], string]>([ + ['canIsolateHost', 'writeHostIsolation'], + ['canUnIsolateHost', 'writeHostIsolation'], + ['canKillProcess', 'writeProcessOperations'], + ['canSuspendProcess', 'writeProcessOperations'], + ['canGetRunningProcesses', 'writeProcessOperations'], + ])('%s should be true if `packagePrivilege.%s` is `true`', (auth) => { + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz[auth]).toBe(true); }); - describe('canUnIsolateHost', () => { - it('should be true if packagePrivilege.writeHostIsolation is true', () => { - fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = - true; - const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); - expect(authz.canUnIsolateHost).toBe(true); - }); - - it('should be false if packagePrivilege.writeHostIsolation is false', () => { - fleetAuthz.packagePrivileges!.endpoint.actions.writeHostIsolation.executePackageAction = - false; - const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); - expect(authz.canUnIsolateHost).toBe(false); - }); + it.each<[EndpointAuthzKeyList[number], string]>([ + ['canIsolateHost', 'writeHostIsolation'], + ['canUnIsolateHost', 'writeHostIsolation'], + ['canKillProcess', 'writeProcessOperations'], + ['canSuspendProcess', 'writeProcessOperations'], + ['canGetRunningProcesses', 'writeProcessOperations'], + ])('%s should be false if `packagePrivilege.%s` is `false`', (auth, privilege) => { + fleetAuthz.packagePrivileges!.endpoint.actions[privilege].executePackageAction = false; + const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); + expect(authz[auth]).toBe(false); }); }); }); diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts index bc40bd11f5d7..9e280f383cae 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts @@ -22,7 +22,6 @@ export const calculateEndpointAuthz = ( licenseService: LicenseService, fleetAuthz: FleetAuthz, userRoles: MaybeImmutable, - // to be used in follow-up PRs isEndpointRbacEnabled: boolean = false ): EndpointAuthz => { const isPlatinumPlusLicense = licenseService.isPlatinumPlus(); @@ -32,17 +31,21 @@ export const calculateEndpointAuthz = ( ? fleetAuthz.packagePrivileges?.endpoint?.actions?.writeHostIsolation?.executePackageAction || false : hasEndpointManagementAccess; + const canWriteProcessOperations = isEndpointRbacEnabled + ? fleetAuthz.packagePrivileges?.endpoint?.actions?.writeProcessOperations + ?.executePackageAction || false + : hasEndpointManagementAccess; return { canAccessFleet: fleetAuthz?.fleet.all ?? userRoles.includes('superuser'), canAccessEndpointManagement: hasEndpointManagementAccess, canCreateArtifactsByPolicy: hasEndpointManagementAccess && isPlatinumPlusLicense, // Response Actions - canIsolateHost: isPlatinumPlusLicense && canIsolateHost, + canIsolateHost: canIsolateHost && isPlatinumPlusLicense, canUnIsolateHost: canIsolateHost, - canKillProcess: hasEndpointManagementAccess && isEnterpriseLicense, - canSuspendProcess: hasEndpointManagementAccess && isEnterpriseLicense, - canGetRunningProcesses: hasEndpointManagementAccess && isEnterpriseLicense, + canKillProcess: canWriteProcessOperations && isEnterpriseLicense, + canSuspendProcess: canWriteProcessOperations && isEnterpriseLicense, + canGetRunningProcesses: canWriteProcessOperations && isEnterpriseLicense, canAccessResponseConsole: hasEndpointManagementAccess && isEnterpriseLicense, }; }; From b5c18d14779fd81f2fe6b521533a413f46446ece Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 17:15:26 +0200 Subject: [PATCH 125/185] Update dependency cypress to ^10.9.0 (#141171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Patryk Kopyciński --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 75751ce826df..e7b1f86b9c05 100644 --- a/package.json +++ b/package.json @@ -1286,7 +1286,7 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "^10.7.0", + "cypress": "^10.9.0", "cypress-axe": "^1.0.0", "cypress-file-upload": "^5.0.8", "cypress-multi-reporters": "^1.6.1", diff --git a/yarn.lock b/yarn.lock index 2902890199d3..968956a15ccf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12559,10 +12559,10 @@ cypress-recurse@^1.23.0: resolved "https://registry.yarnpkg.com/cypress-recurse/-/cypress-recurse-1.23.0.tgz#f87334747516de6737bc4708754e8f429057bc6d" integrity sha512-CAsdvynhuR3SUEXVJRO2jBEnZRJ6nJp7nMXHwzV4UQq9Lap3Bj72AwcJK0cl51fJXcTaGDXYTQQ9zvGe3TyaQA== -cypress@^10.7.0: - version "10.7.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.7.0.tgz#2d37f8b9751c6de33ee48639cb7e67a2ce593231" - integrity sha512-gTFvjrUoBnqPPOu9Vl5SBHuFlzx/Wxg/ZXIz2H4lzoOLFelKeF7mbwYUOzgzgF0oieU2WhJAestQdkgwJMMTvQ== +cypress@^10.9.0: + version "10.9.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.9.0.tgz#273a61a6304766f9d6423e5ac8d4a9a11ed8b485" + integrity sha512-MjIWrRpc+bQM9U4kSSdATZWZ2hUqHGFEQTF7dfeZRa4MnalMtc88FIE49USWP2ZVtfy5WPBcgfBX+YorFqGElA== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -12583,7 +12583,7 @@ cypress@^10.7.0: dayjs "^1.10.4" debug "^4.3.2" enquirer "^2.3.6" - eventemitter2 "^6.4.3" + eventemitter2 "6.4.7" execa "4.1.0" executable "^4.1.1" extract-zip "2.0.1" @@ -14679,10 +14679,10 @@ eventemitter-asyncresource@^1.0.0: resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b" integrity sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ== -eventemitter2@^6.4.3: - version "6.4.3" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.3.tgz#35c563619b13f3681e7eb05cbdaf50f56ba58820" - integrity sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ== +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== eventemitter3@^4.0.0: version "4.0.7" From 49d0858a406d1c6854b2305263141bb5ab9e8f0d Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 29 Sep 2022 08:19:50 -0700 Subject: [PATCH 126/185] [Security Solution][Exceptions] - Fixes up some bugs in the all exception items view (#141682) ### Summary Addresses #140709, #141056, #141421, #141042 --- .../all_exception_items_table/index.tsx | 36 ++++++++++++++----- .../all_exception_items_table/search_bar.tsx | 9 ++--- .../all_exception_items_table/translations.ts | 25 +++++++++++-- .../logic/use_find_references.tsx | 8 +++-- .../rules/all/exceptions/translations.ts | 4 +-- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 8 files changed, 64 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx index 9ba216b80f14..d090f5a3bc59 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx @@ -6,20 +6,21 @@ */ import React, { useCallback, useMemo, useEffect, useReducer } from 'react'; -import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; import type { ExceptionListItemSchema, UseExceptionListItemsSuccess, Pagination, - ExceptionListTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { transformInput } from '@kbn/securitysolution-list-hooks'; import { deleteExceptionListItemById, fetchExceptionListsItemsByListIds, } from '@kbn/securitysolution-list-api'; +import styled from 'styled-components'; import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; import { useUserData } from '../../../../detections/components/user_info'; import { useKibana, useToasts } from '../../../../common/lib/kibana'; @@ -37,6 +38,10 @@ import * as i18n from './translations'; import { useFindExceptionListReferences } from '../../logic/use_find_references'; import type { Rule } from '../../../../detections/containers/detection_engine/rules/types'; +const StyledText = styled(EuiText)` + font-style: italic; +`; + const STATES_SEARCH_HIDDEN: ViewerState[] = ['error', 'empty']; const STATES_PAGINATION_UTILITY_HIDDEN: ViewerState[] = [ 'loading', @@ -51,7 +56,7 @@ const initialState: State = { pageIndex: 0, pageSize: 25, totalItemCount: 0, - pageSizeOptions: [1, 5, 10, 25, 50, 100, 200, 300], + pageSizeOptions: [5, 10, 25, 50, 100, 200, 300], }, exceptions: [], exceptionToEdit: null, @@ -154,7 +159,18 @@ const ExceptionsViewerComponent = ({ [dispatch] ); - const [_, allReferences] = useFindExceptionListReferences(exceptionListsToQuery); + const [isLoadingReferences, isFetchReferencesError, allReferences] = + useFindExceptionListReferences(exceptionListsToQuery); + + useEffect(() => { + if (isFetchReferencesError) { + setViewerState('error'); + } else if (viewerState == null && isLoadingReferences) { + setViewerState('loading'); + } else if (viewerState === 'loading' && !isLoadingReferences) { + setViewerState(null); + } + }, [isLoadingReferences, isFetchReferencesError, setViewerState, viewerState]); const handleFetchItems = useCallback( async (options?: GetExceptionItemProps) => { @@ -212,12 +228,8 @@ const ExceptionsViewerComponent = ({ const handleGetExceptionListItems = useCallback( async (options?: GetExceptionItemProps) => { try { - setViewerState('loading'); - const { pageIndex, itemsPerPage, total, data } = await handleFetchItems(options); - setViewerState(total > 0 ? null : 'empty'); - setExceptions({ exceptions: data, pagination: { @@ -226,6 +238,8 @@ const ExceptionsViewerComponent = ({ total, }, }); + + setViewerState(total > 0 ? null : 'empty'); } catch (e) { setViewerState('error'); @@ -367,6 +381,12 @@ const ExceptionsViewerComponent = ({ <> + + {listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ENDPOINT_EXCEPTIONS_TAB_ABOUT + : i18n.EXCEPTIONS_TAB_ABOUT} + + {!STATES_SEARCH_HIDDEN.includes(viewerState) && ( { return listType === ExceptionListTypeEnum.ENDPOINT - ? i18n.ADD_TO_ENDPOINT_LIST - : i18n.ADD_TO_DETECTIONS_LIST; + ? sharedI18n.ADD_TO_ENDPOINT_LIST + : sharedI18n.ADD_TO_DETECTIONS_LIST; }, [listType]); return ( @@ -84,7 +85,7 @@ const ExceptionsViewerSearchBarComponent = ({ values: { itemName }, defaultMessage: '"{itemName}" deleted successfully.', }); + +export const ENDPOINT_EXCEPTIONS_TAB_ABOUT = i18n.translate( + 'xpack.securitySolution.exceptions.allExceptionItems.exceptionEndpointDetailsDescription', + { + defaultMessage: + 'Endpoint exceptions are added to both the detection rule and the Elastic Endpoint agent on your hosts.', + } +); + +export const EXCEPTIONS_TAB_ABOUT = i18n.translate( + 'xpack.securitySolution.exceptions.allExceptionItems.exceptionDetectionDetailsDescription', + { + defaultMessage: 'Rule exceptions are added to the detection rule.', + } +); + +export const SEARCH_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.exceptions.allExceptionItems.searchPlaceholder', + { + defaultMessage: 'Filter exceptions using simple query syntax, for example, name:"my list"', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx index f30d5aeb598a..f7628f0014d2 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/logic/use_find_references.tsx @@ -14,7 +14,7 @@ import { useToasts } from '../../../common/lib/kibana'; import type { FindRulesReferencedByExceptionsListProp } from '../../../detections/containers/detection_engine/rules/types'; import * as i18n from '../utils/translations'; -export type ReturnUseFindExceptionListReferences = [boolean, RuleReferences | null]; +export type ReturnUseFindExceptionListReferences = [boolean, boolean, RuleReferences | null]; export interface RuleReferences { [key: string]: RuleReferenceSchema[]; @@ -28,6 +28,7 @@ export const useFindExceptionListReferences = ( ): ReturnUseFindExceptionListReferences => { const toasts = useToasts(); const [isLoading, setIsLoading] = useState(false); + const [errorExists, setErrorExists] = useState(false); const [references, setReferences] = useState(null); const listRefs = useMemo((): FindRulesReferencedByExceptionsListProp[] => { return ruleExceptionLists.map((list) => { @@ -61,11 +62,13 @@ export const useFindExceptionListReferences = ( }, {}); if (isSubscribed) { + setErrorExists(false); setIsLoading(false); setReferences(results); } } catch (error) { if (isSubscribed) { + setErrorExists(true); setIsLoading(false); toasts.addError(error, { title: i18n.ERROR_FETCHING_REFERENCES_TITLE }); } @@ -73,6 +76,7 @@ export const useFindExceptionListReferences = ( }; if (listRefs.length === 0 && isSubscribed) { + setErrorExists(false); setIsLoading(false); setReferences(null); } else { @@ -85,5 +89,5 @@ export const useFindExceptionListReferences = ( }; }, [ruleExceptionLists, listRefs, toasts]); - return [isLoading, references]; + return [isLoading, errorExists, references]; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts index b22d4030384a..8baf21db15c0 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts @@ -155,9 +155,9 @@ export const referenceErrorMessage = (referenceCount: number) => }); export const EXCEPTION_LIST_SEARCH_PLACEHOLDER = i18n.translate( - 'xpack.securitySolution.exceptions.searchPlaceholder', + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.searchPlaceholder', { - defaultMessage: 'e.g. Example List Name', + defaultMessage: 'Search by name or list_id', } ); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 60b692e720ec..09d5ebabd0d0 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -28301,7 +28301,6 @@ "xpack.securitySolution.exceptions.referenceModalCancelButton": "Annuler", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "Retirer la liste d'exceptions", "xpack.securitySolution.exceptions.referenceModalTitle": "Retirer la liste d'exceptions", - "xpack.securitySolution.exceptions.searchPlaceholder": "par ex. Exemple de liste de noms", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "Ajouter un nouveau commentaire...", "xpack.securitySolution.exceptions.viewer.addToClipboard": "Commentaire", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "Ajouter une exception à une règle", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0aa5fa39b93e..32c2dbbf5f01 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -28276,7 +28276,6 @@ "xpack.securitySolution.exceptions.referenceModalCancelButton": "キャンセル", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "例外リストを削除", "xpack.securitySolution.exceptions.referenceModalTitle": "例外リストを削除", - "xpack.securitySolution.exceptions.searchPlaceholder": "例:例外リスト名", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "新しいコメントを追加...", "xpack.securitySolution.exceptions.viewer.addToClipboard": "コメント", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "ルール例外の追加", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index baf98e70b8dd..48fe03474e19 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -28310,7 +28310,6 @@ "xpack.securitySolution.exceptions.referenceModalCancelButton": "取消", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "移除例外列表", "xpack.securitySolution.exceptions.referenceModalTitle": "移除例外列表", - "xpack.securitySolution.exceptions.searchPlaceholder": "例如,示例列表名称", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "添加新注释......", "xpack.securitySolution.exceptions.viewer.addToClipboard": "注释", "xpack.securitySolution.exceptions.viewer.addToDetectionsListLabel": "添加规则例外", From 27916d316a0268ee6bc86807f6bef16c93f4fcd6 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Thu, 29 Sep 2022 11:23:21 -0400 Subject: [PATCH 127/185] [Synthetics] enable/disable - prevent incorrect keys from being added to the monitor saved object (#140553) * synthetics - enable/disable - prevent incorrect keys from being added to the monitor saved object * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * adjust types * add exact typing * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * adjust test * use exact types * use exact types for editing * adjust types * adjust tests * adjust types * Update x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts * adjust normalizers * Update x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts * Update x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts * adjust types * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * adjust jest tests * adjust types * adjust api_integration tests * update tests Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/constants/monitor_defaults.ts | 2 + .../common/formatters/browser/formatters.ts | 4 +- .../common/formatters/common/formatters.ts | 2 + .../common/formatters/http/formatters.ts | 1 + .../common/formatters/tcp/formatters.ts | 1 + .../monitor_management/monitor_types.ts | 19 ++- .../monitor_add_edit/form/formatter.test.tsx | 2 + .../monitor_list_table/monitor_enabled.tsx | 2 +- .../overview/actions_popover.test.tsx | 20 +-- .../overview/overview/actions_popover.tsx | 2 +- .../hooks/use_monitor_enable_handler.tsx | 10 +- .../synthetics/state/monitor_list/actions.ts | 3 +- .../apps/synthetics/state/monitor_list/api.ts | 3 +- .../__mocks__/synthetics_store.mock.ts | 6 +- .../fleet_package/browser/normalizers.ts | 4 +- .../fleet_package/common/normalizers.ts | 2 + .../fleet_package/http/normalizers.ts | 1 + .../fleet_package/tcp/normalizers.ts | 1 + .../routes/monitor_cruds/add_monitor.ts | 9 +- .../routes/monitor_cruds/edit_monitor.ts | 6 +- .../monitor_cruds/monitor_validation.test.ts | 5 +- .../monitor_cruds/monitor_validation.ts | 20 ++- .../synthetics_service/run_once_monitor.ts | 4 +- .../synthetics_service/formatters/browser.ts | 4 +- .../synthetics_service/formatters/common.ts | 2 + .../synthetics_service/formatters/http.ts | 1 + .../synthetics_service/formatters/tcp.ts | 1 + .../normalizers/http_monitor.test.ts | 2 + .../normalizers/icmp_monitor.test.ts | 4 +- .../normalizers/tcp_monitor.test.ts | 7 +- .../project_monitor_formatter.ts | 23 ++- .../apis/uptime/rest/add_monitor.ts | 35 ++++ .../apis/uptime/rest/add_monitor_project.ts | 149 +++++++++++++++++- .../apis/uptime/rest/edit_monitor.ts | 62 +++++++- .../uptime/rest/fixtures/http_monitor.json | 1 + .../uptime/rest/fixtures/icmp_monitor.json | 21 +-- .../uptime/rest/fixtures/tcp_monitor.json | 1 + 37 files changed, 339 insertions(+), 103 deletions(-) diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 1d142bbe33c6..a926cba109e6 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -108,6 +108,7 @@ export const DEFAULT_HTTP_SIMPLE_FIELDS: HTTPSimpleFields = { [ConfigKey.MAX_REDIRECTS]: '0', [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, + [ConfigKey.PORT]: null, }; export const DEFAULT_HTTP_ADVANCED_FIELDS: HTTPAdvancedFields = { @@ -144,6 +145,7 @@ export const DEFAULT_TCP_SIMPLE_FIELDS: TCPSimpleFields = { [ConfigKey.HOSTS]: '', [ConfigKey.MONITOR_TYPE]: DataStream.TCP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, + [ConfigKey.PORT]: null, }; export const DEFAULT_TCP_ADVANCED_FIELDS: TCPAdvancedFields = { diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index 78e55e97d5b0..409f2b44343e 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -18,6 +18,7 @@ import { tlsValueToStringFormatter, tlsArrayToYamlFormatter, } from '../tls/formatters'; +import { tlsFormatters } from '../tls/formatters'; export type BrowserFormatMap = Record; @@ -72,9 +73,8 @@ export const browserFormatters: BrowserFormatMap = { arrayToJsonFormatter(fields[ConfigKey.JOURNEY_FILTERS_TAGS]), [ConfigKey.THROTTLING_CONFIG]: throttlingFormatter, [ConfigKey.IGNORE_HTTPS_ERRORS]: null, - [ConfigKey.PROJECT_ID]: null, [ConfigKey.PLAYWRIGHT_OPTIONS]: null, - [ConfigKey.ORIGINAL_SPACE]: null, [ConfigKey.TEXT_ASSERTION]: null, ...commonFormatters, + ...tlsFormatters, }; diff --git a/x-pack/plugins/synthetics/common/formatters/common/formatters.ts b/x-pack/plugins/synthetics/common/formatters/common/formatters.ts index 751721e5d703..739c7184e722 100644 --- a/x-pack/plugins/synthetics/common/formatters/common/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/common/formatters.ts @@ -29,7 +29,9 @@ export const commonFormatters: CommonFormatMap = { [ConfigKey.MONITOR_SOURCE_TYPE]: null, [ConfigKey.FORM_MONITOR_TYPE]: null, [ConfigKey.JOURNEY_ID]: null, + [ConfigKey.PROJECT_ID]: null, [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, + [ConfigKey.ORIGINAL_SPACE]: null, }; export const arrayToJsonFormatter = (value: string[] = []) => diff --git a/x-pack/plugins/synthetics/common/formatters/http/formatters.ts b/x-pack/plugins/synthetics/common/formatters/http/formatters.ts index 0dc9b795717a..5eeb5888255d 100644 --- a/x-pack/plugins/synthetics/common/formatters/http/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/http/formatters.ts @@ -41,6 +41,7 @@ export const httpFormatters: HTTPFormatMap = { [ConfigKey.REQUEST_HEADERS_CHECK]: (fields) => objectToJsonFormatter(fields[ConfigKey.REQUEST_HEADERS_CHECK]), [ConfigKey.REQUEST_METHOD_CHECK]: null, + [ConfigKey.PORT]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts b/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts index 5b3737229a12..bec7ceb44484 100644 --- a/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts @@ -19,6 +19,7 @@ export const tcpFormatters: TCPFormatMap = { [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: null, [ConfigKey.RESPONSE_RECEIVE_CHECK]: null, [ConfigKey.REQUEST_SEND_CHECK]: null, + [ConfigKey.PORT]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 46bc0f135452..bbb6eb1bb30d 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -83,6 +83,8 @@ export const CommonFieldsCodec = t.intersection([ [ConfigKey.MONITOR_SOURCE_TYPE]: SourceTypeCodec, [ConfigKey.CONFIG_ID]: t.string, [ConfigKey.JOURNEY_ID]: t.string, + [ConfigKey.PROJECT_ID]: t.string, + [ConfigKey.ORIGINAL_SPACE]: t.string, [ConfigKey.CUSTOM_HEARTBEAT_ID]: t.string, }), ]); @@ -94,6 +96,7 @@ export const TCPSimpleFieldsCodec = t.intersection([ t.interface({ [ConfigKey.METADATA]: MetadataCodec, [ConfigKey.HOSTS]: t.string, + [ConfigKey.PORT]: t.union([t.number, t.null]), }), CommonFieldsCodec, ]); @@ -151,6 +154,7 @@ export const HTTPSimpleFieldsCodec = t.intersection([ [ConfigKey.METADATA]: MetadataCodec, [ConfigKey.MAX_REDIRECTS]: t.string, [ConfigKey.URLS]: t.string, + [ConfigKey.PORT]: t.union([t.number, t.null]), }), CommonFieldsCodec, ]); @@ -217,8 +221,6 @@ export const EncryptedBrowserSimpleFieldsCodec = t.intersection([ }), t.partial({ [ConfigKey.PLAYWRIGHT_OPTIONS]: t.string, - [ConfigKey.PROJECT_ID]: t.string, - [ConfigKey.ORIGINAL_SPACE]: t.string, [ConfigKey.TEXT_ASSERTION]: t.string, }), ]), @@ -241,7 +243,7 @@ export const BrowserSensitiveSimpleFieldsCodec = t.intersection([ CommonFieldsCodec, ]); -export const BrowserAdvancedFieldsCodec = t.interface({ +export const EncryptedBrowserAdvancedFieldsCodec = t.interface({ [ConfigKey.SCREENSHOTS]: t.string, [ConfigKey.JOURNEY_FILTERS_MATCH]: t.string, [ConfigKey.JOURNEY_FILTERS_TAGS]: t.array(t.string), @@ -263,25 +265,26 @@ export const BrowserSensitiveAdvancedFieldsCodec = t.interface({ [ConfigKey.SYNTHETICS_ARGS]: t.array(t.string), }); -export const BrowserAdvancedsCodec = t.intersection([ - BrowserAdvancedFieldsCodec, +export const BrowserAdvancedFieldsCodec = t.intersection([ + EncryptedBrowserAdvancedFieldsCodec, BrowserSensitiveAdvancedFieldsCodec, ]); export const EncryptedBrowserFieldsCodec = t.intersection([ EncryptedBrowserSimpleFieldsCodec, - BrowserAdvancedFieldsCodec, + EncryptedBrowserAdvancedFieldsCodec, + TLSFieldsCodec, ]); export const BrowserFieldsCodec = t.intersection([ BrowserSimpleFieldsCodec, BrowserAdvancedFieldsCodec, - BrowserSensitiveAdvancedFieldsCodec, + TLSCodec, ]); export type BrowserFields = t.TypeOf; export type BrowserSimpleFields = t.TypeOf; -export type BrowserAdvancedFields = t.TypeOf; +export type BrowserAdvancedFields = t.TypeOf; // MonitorFields, represents any possible monitor type export const MonitorFieldsCodec = t.intersection([ diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx index 4b4e778b87b9..6ff7cd651b33 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx @@ -134,6 +134,7 @@ describe('format', () => { timeout: '16', type: 'http', urls: 'sample url', + 'url.port': null, username: '', }); }); @@ -347,6 +348,7 @@ describe('format', () => { timeout: '16', type: 'http', urls: 'sample url', + 'url.port': null, username: '', }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx index be1123b12d41..e5e7c6bbe378 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/monitor_enabled.tsx @@ -51,7 +51,7 @@ export const MonitorEnabled = ({ const handleEnabledChange = (event: EuiSwitchEvent) => { const checked = event.target.checked; - updateMonitorEnabledState(monitor, checked); + updateMonitorEnabledState(checked); }; return ( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx index af6799068c27..d37fb8c2a300 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx @@ -111,15 +111,7 @@ describe('ActionsPopover', () => { const enableButton = getByText('Disable monitor'); fireEvent.click(enableButton); expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1); - expect(updateMonitorEnabledState.mock.calls[0]).toEqual([ - { - id: 'somelongstring', - isEnabled: true, - location: { id: 'us_central', isServiceManaged: true }, - name: 'Monitor 1', - }, - false, - ]); + expect(updateMonitorEnabledState.mock.calls[0]).toEqual([false]); }); it('sets enabled state to true', async () => { @@ -139,14 +131,6 @@ describe('ActionsPopover', () => { const enableButton = getByText('Enable monitor'); fireEvent.click(enableButton); expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1); - expect(updateMonitorEnabledState.mock.calls[0]).toEqual([ - { - id: 'somelongstring', - isEnabled: false, - location: { id: 'us_central', isServiceManaged: true }, - name: 'Monitor 1', - }, - true, - ]); + expect(updateMonitorEnabledState.mock.calls[0]).toEqual([true]); }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx index 932c6344c471..1267c2ab43c5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx @@ -141,7 +141,7 @@ export function ActionsPopover({ icon: 'invert', onClick: () => { if (status !== FETCH_STATUS.LOADING) - updateMonitorEnabledState(monitor, !monitor.isEnabled); + updateMonitorEnabledState(!monitor.isEnabled); }, }, ], diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_monitor_enable_handler.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_monitor_enable_handler.tsx index 6fed27f2df6b..e00b54ec4b75 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_monitor_enable_handler.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_monitor_enable_handler.tsx @@ -9,11 +9,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { FETCH_STATUS } from '@kbn/observability-plugin/public'; import React, { useCallback, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { - ConfigKey, - EncryptedSyntheticsMonitor, - MonitorOverviewItem, -} from '../components/monitors_page/overview/types'; +import { ConfigKey } from '../components/monitors_page/overview/types'; import { clearMonitorUpsertStatus, fetchUpsertMonitorAction, @@ -41,11 +37,11 @@ export function useMonitorEnableHandler({ const savedObjEnabledState = upsertStatuses[id]?.enabled; const [isEnabled, setIsEnabled] = useState(null); const updateMonitorEnabledState = useCallback( - (monitor: EncryptedSyntheticsMonitor | MonitorOverviewItem, enabled: boolean) => { + (enabled: boolean) => { dispatch( fetchUpsertMonitorAction({ id, - monitor: { ...monitor, [ConfigKey.ENABLED]: enabled }, + monitor: { [ConfigKey.ENABLED]: enabled }, }) ); }, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/actions.ts index 3315719a6bb1..fcfc3d4f22cf 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/actions.ts @@ -10,7 +10,6 @@ import { createAction } from '@reduxjs/toolkit'; import { EncryptedSyntheticsMonitor, MonitorManagementListResult, - MonitorOverviewItem, } from '../../../../../common/runtime_types'; import { createAsyncAction } from '../utils/actions'; @@ -23,7 +22,7 @@ export const fetchMonitorListAction = createAsyncAction< export interface UpsertMonitorRequest { id: string; - monitor: EncryptedSyntheticsMonitor | MonitorOverviewItem; + monitor: Partial; } export const fetchUpsertMonitorAction = createAction('fetchUpsertMonitor'); export const fetchUpsertSuccessAction = createAction<{ diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/api.ts index 5e4e2e1bc1a6..f6a99fd3e02e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/api.ts @@ -11,7 +11,6 @@ import { FetchMonitorManagementListQueryArgs, MonitorManagementListResult, MonitorManagementListResultCodec, - MonitorOverviewItem, ServiceLocationErrors, SyntheticsMonitor, } from '../../../../../common/runtime_types'; @@ -55,7 +54,7 @@ export const fetchUpsertMonitor = async ({ monitor, id, }: { - monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor | MonitorOverviewItem; + monitor: Partial | Partial; id?: string; }): Promise<{ attributes: { errors: ServiceLocationErrors } } | SyntheticsMonitor> => { if (id) { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index 57ce5e39a8db..5fa93522e6c9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -13,6 +13,8 @@ import { LocationStatus, ScheduleUnit, SourceType, + VerificationMode, + TLSVersion, } from '../../../../../../common/runtime_types'; /** @@ -338,8 +340,8 @@ function getMonitorDetailsMockSlice() { 'ssl.certificate': '', 'ssl.key': '', 'ssl.key_passphrase': '', - 'ssl.verification_mode': 'full', - 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': VerificationMode.FULL, + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'] as TLSVersion[], revision: 1, updated_at: '2022-07-24T17:15:46.342Z', }, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts index 61f978327b8a..d1d9917f19c3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts @@ -18,6 +18,7 @@ import { getNormalizer, getJsonToJavascriptNormalizer, } from '../common/normalizers'; +import { tlsNormalizers } from '../tls/normalizers'; import { defaultBrowserSimpleFields, defaultBrowserAdvancedFields } from '../contexts'; @@ -107,9 +108,8 @@ export const browserNormalizers: BrowserNormalizerMap = { ConfigKey.JOURNEY_FILTERS_TAGS ), [ConfigKey.IGNORE_HTTPS_ERRORS]: getBrowserNormalizer(ConfigKey.IGNORE_HTTPS_ERRORS), - [ConfigKey.PROJECT_ID]: getBrowserNormalizer(ConfigKey.PROJECT_ID), [ConfigKey.PLAYWRIGHT_OPTIONS]: getBrowserNormalizer(ConfigKey.PLAYWRIGHT_OPTIONS), - [ConfigKey.ORIGINAL_SPACE]: getBrowserNormalizer(ConfigKey.ORIGINAL_SPACE), [ConfigKey.TEXT_ASSERTION]: getBrowserNormalizer(ConfigKey.TEXT_ASSERTION), ...commonNormalizers, + ...tlsNormalizers, }; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts index 630ac70a8e05..d05730c5dbe1 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts @@ -93,5 +93,7 @@ export const commonNormalizers: CommonNormalizerMap = { [ConfigKey.MONITOR_SOURCE_TYPE]: getCommonNormalizer(ConfigKey.MONITOR_SOURCE_TYPE), [ConfigKey.FORM_MONITOR_TYPE]: getCommonNormalizer(ConfigKey.FORM_MONITOR_TYPE), [ConfigKey.JOURNEY_ID]: getCommonNormalizer(ConfigKey.JOURNEY_ID), + [ConfigKey.PROJECT_ID]: getCommonNormalizer(ConfigKey.PROJECT_ID), [ConfigKey.CUSTOM_HEARTBEAT_ID]: getCommonNormalizer(ConfigKey.CUSTOM_HEARTBEAT_ID), + [ConfigKey.ORIGINAL_SPACE]: getCommonNormalizer(ConfigKey.ORIGINAL_SPACE), }; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts index f7e7ad3eeac2..a783639f1ab1 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts @@ -34,6 +34,7 @@ export const getHTTPJsonToJavascriptNormalizer = (key: ConfigKey) => { export const httpNormalizers: HTTPNormalizerMap = { [ConfigKey.METADATA]: getHTTPJsonToJavascriptNormalizer(ConfigKey.METADATA), [ConfigKey.URLS]: getHTTPNormalizer(ConfigKey.URLS), + [ConfigKey.PORT]: getHTTPNormalizer(ConfigKey.PORT), [ConfigKey.MAX_REDIRECTS]: getHTTPNormalizer(ConfigKey.MAX_REDIRECTS), [ConfigKey.USERNAME]: getHTTPNormalizer(ConfigKey.USERNAME), [ConfigKey.PASSWORD]: getHTTPNormalizer(ConfigKey.PASSWORD), diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts index ae36de49fb57..86efeeae6920 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts @@ -33,6 +33,7 @@ export const getTCPJsonToJavascriptNormalizer = (key: ConfigKey) => { export const tcpNormalizers: TCPNormalizerMap = { [ConfigKey.METADATA]: getTCPJsonToJavascriptNormalizer(ConfigKey.METADATA), [ConfigKey.HOSTS]: getTCPNormalizer(ConfigKey.HOSTS), + [ConfigKey.PORT]: getTCPNormalizer(ConfigKey.PORT), [ConfigKey.PROXY_URL]: getTCPNormalizer(ConfigKey.PROXY_URL), [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: getTCPNormalizer(ConfigKey.PROXY_USE_LOCAL_RESOLVER), [ConfigKey.RESPONSE_RECEIVE_CHECK]: getTCPNormalizer(ConfigKey.RESPONSE_RECEIVE_CHECK), diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts index 1016e37b87e0..1f9a55e34761 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts @@ -67,7 +67,7 @@ export const addSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ const validationResult = validateMonitor(monitorWithDefaults as MonitorFields); - if (!validationResult.valid) { + if (!validationResult.valid || !validationResult.decodedMonitor) { const { reason: message, details, payload } = validationResult; return response.badRequest({ body: { message, attributes: { details, ...payload } } }); } @@ -78,8 +78,7 @@ export const addSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ try { const { errors, newMonitor } = await syncNewMonitor({ - normalizedMonitor: monitorWithDefaults, - monitor, + normalizedMonitor: validationResult.decodedMonitor, server, syntheticsMonitorClient, savedObjectsClient, @@ -140,7 +139,6 @@ export const createNewSavedObjectMonitor = async ({ export const syncNewMonitor = async ({ id, - monitor, server, syntheticsMonitorClient, savedObjectsClient, @@ -150,7 +148,6 @@ export const syncNewMonitor = async ({ spaceId, }: { id?: string; - monitor: SyntheticsMonitor; normalizedMonitor: SyntheticsMonitor; server: UptimeServerSetup; syntheticsMonitorClient: SyntheticsMonitorClient; @@ -201,7 +198,7 @@ export const syncNewMonitor = async ({ formatTelemetryEvent({ errors: syncErrors, monitor: monitorSavedObject, - isInlineScript: Boolean((monitor as MonitorFields)[ConfigKey.SOURCE_INLINE]), + isInlineScript: Boolean((normalizedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]), kibanaVersion: server.kibanaVersion, }) ); diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts index a16a1ba7089e..d6b50880ed35 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts @@ -84,13 +84,13 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ( const validationResult = validateMonitor(editedMonitor as MonitorFields); - if (!validationResult.valid) { + if (!validationResult.valid || !validationResult.decodedMonitor) { const { reason: message, details, payload } = validationResult; return response.badRequest({ body: { message, attributes: { details, ...payload } } }); } const monitorWithRevision = { - ...editedMonitor, + ...validationResult.decodedMonitor, revision: (previousMonitor.attributes[ConfigKey.REVISION] || 0) + 1, }; const formattedMonitor = formatSecrets(monitorWithRevision); @@ -102,7 +102,7 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ( syntheticsMonitorClient, savedObjectsClient, request, - normalizedMonitor: editedMonitor, + normalizedMonitor: validationResult.decodedMonitor, monitorWithRevision: formattedMonitor, spaceId, }); diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts index 8a532279dda8..76bda0f4a2f1 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts @@ -109,6 +109,7 @@ describe('validateMonitor', () => { [ConfigKey.METADATA]: testMetaData, [ConfigKey.HOSTS]: 'https://host1.com', [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, + [ConfigKey.PORT]: null, }; testTCPAdvancedFields = { @@ -131,6 +132,7 @@ describe('validateMonitor', () => { [ConfigKey.MAX_REDIRECTS]: '3', [ConfigKey.URLS]: 'https://example.com', [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, + [ConfigKey.PORT]: null, }; testHTTPAdvancedFields = { @@ -453,7 +455,8 @@ function getJsonPayload() { ' },' + ' "url": "https://example-url.com",' + ' "isServiceManaged": true' + - ' }]' + + ' }],' + + ' "url.port": null' + '}'; return JSON.parse(json); diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts index d648722e0d5c..77cd0fa23a7c 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import * as t from 'io-ts'; import { isLeft } from 'fp-ts/lib/Either'; import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; @@ -19,6 +20,7 @@ import { ICMPSimpleFieldsCodec, MonitorFields, TCPFieldsCodec, + SyntheticsMonitor, } from '../../../common/runtime_types'; type MonitorCodecType = @@ -39,6 +41,7 @@ export interface ValidationResult { reason: string; details: string; payload: object; + decodedMonitor?: SyntheticsMonitor; } /** @@ -58,9 +61,10 @@ export function validateMonitor(monitorFields: MonitorFields): ValidationResult }; } - const codec = monitorTypeToCodecMap[monitorType]; + // Cast it to ICMPCodec to satisfy typing. During runtime, correct codec will be used to decode. + const SyntheticsMonitorCodec = monitorTypeToCodecMap[monitorType] as typeof ICMPSimpleFieldsCodec; - if (!codec) { + if (!SyntheticsMonitorCodec) { return { valid: false, reason: `Payload is not a valid monitor object`, @@ -69,8 +73,8 @@ export function validateMonitor(monitorFields: MonitorFields): ValidationResult }; } - // Cast it to ICMPCodec to satisfy typing. During runtime, correct codec will be used to decode. - const decodedMonitor = (codec as typeof ICMPSimpleFieldsCodec).decode(monitorFields); + const ExactSyntheticsMonitorCodec = t.exact(SyntheticsMonitorCodec); + const decodedMonitor = ExactSyntheticsMonitorCodec.decode(monitorFields); if (isLeft(decodedMonitor)) { return { @@ -81,7 +85,13 @@ export function validateMonitor(monitorFields: MonitorFields): ValidationResult }; } - return { valid: true, reason: '', details: '', payload: monitorFields }; + return { + valid: true, + reason: '', + details: '', + payload: monitorFields, + decodedMonitor: decodedMonitor.right, + }; } export function validateProjectMonitor(monitorFields: ProjectMonitor): ValidationResult { diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts index 394567f45b60..5bbc8c27e3eb 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts @@ -26,7 +26,7 @@ export const runOnceSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = const validationResult = validateMonitor(monitor); - if (!validationResult.valid) { + if (!validationResult.valid || !validationResult.decodedMonitor) { const { reason: message, details, payload } = validationResult; return response.badRequest({ body: { message, attributes: { details, ...payload } } }); } @@ -36,7 +36,7 @@ export const runOnceSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = const errors = await syntheticsService.runOnceConfigs([ formatHeartbeatRequest({ // making it enabled, even if it's disabled in the UI - monitor: { ...monitor, enabled: true }, + monitor: { ...validationResult.decodedMonitor, enabled: true }, monitorId, runOnce: true, }), diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts index 7095e437aea2..d9ee08ace167 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts @@ -14,6 +14,7 @@ import { } from './common'; import { BrowserFields, ConfigKey } from '../../../common/runtime_types/monitor_management'; import { DEFAULT_BROWSER_ADVANCED_FIELDS } from '../../../common/constants/monitor_defaults'; +import { tlsFormatters } from './tls'; export type BrowserFormatMap = Record; @@ -66,10 +67,9 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKey.JOURNEY_FILTERS_TAGS]: (fields) => arrayFormatter(fields[ConfigKey.JOURNEY_FILTERS_TAGS]), [ConfigKey.IGNORE_HTTPS_ERRORS]: null, - [ConfigKey.PROJECT_ID]: null, [ConfigKey.PLAYWRIGHT_OPTIONS]: (fields) => stringToObjectFormatter(fields[ConfigKey.PLAYWRIGHT_OPTIONS] || ''), - [ConfigKey.ORIGINAL_SPACE]: null, [ConfigKey.TEXT_ASSERTION]: null, ...commonFormatters, + ...tlsFormatters, }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts index f9c3125041b4..a2427357e368 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/common.ts @@ -31,7 +31,9 @@ export const commonFormatters: CommonFormatMap = { fields[ConfigKey.MONITOR_SOURCE_TYPE] || SourceType.UI, [ConfigKey.FORM_MONITOR_TYPE]: null, [ConfigKey.JOURNEY_ID]: null, + [ConfigKey.PROJECT_ID]: null, [ConfigKey.CUSTOM_HEARTBEAT_ID]: null, + [ConfigKey.ORIGINAL_SPACE]: null, }; export const arrayFormatter = (value: string[] = []) => (value.length ? value : null); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts index b8652e0813a0..83545eb198ba 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts @@ -14,6 +14,7 @@ export type HTTPFormatMap = Record; export const httpFormatters: HTTPFormatMap = { [ConfigKey.METADATA]: (fields) => objectFormatter(fields[ConfigKey.METADATA]), [ConfigKey.URLS]: null, + [ConfigKey.PORT]: null, [ConfigKey.MAX_REDIRECTS]: null, [ConfigKey.USERNAME]: null, [ConfigKey.PASSWORD]: null, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/tcp.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/tcp.ts index c295a17ed896..25ba5c08e9b3 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/tcp.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/tcp.ts @@ -14,6 +14,7 @@ export type TCPFormatMap = Record; export const tcpFormatters: TCPFormatMap = { [ConfigKey.METADATA]: null, [ConfigKey.HOSTS]: null, + [ConfigKey.PORT]: null, [ConfigKey.PROXY_URL]: null, [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: null, [ConfigKey.RESPONSE_RECEIVE_CHECK]: null, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts index 922c5ca6dab0..cea5aa8b50de 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts @@ -179,6 +179,7 @@ describe('http normalizers', () => { timeout: '80', type: 'http', urls: 'http://localhost:9200', + 'url.port': null, username: '', }, unsupportedKeys: ['check.response.body', 'unsupportedKey.nestedUnsupportedKey'], @@ -232,6 +233,7 @@ describe('http normalizers', () => { timeout: '80', type: 'http', urls: 'http://localhost:9200', + 'url.port': null, username: '', }, unsupportedKeys: [], diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts index e32ddf4f328a..74ac2cb2bfaf 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts @@ -8,7 +8,7 @@ import { Locations, LocationStatus, PrivateLocation } from '../../../../common/runtime_types'; import { normalizeProjectMonitors } from '.'; -describe('http normalizers', () => { +describe('icmp normalizers', () => { describe('normalize push monitors', () => { const projectId = 'test-project-id'; const locations: Locations = [ @@ -81,7 +81,7 @@ describe('http normalizers', () => { }, ]; - it('properly normalizes http monitors', () => { + it('properly normalizes icmp monitors', () => { const actual = normalizeProjectMonitors({ locations, privateLocations, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts index 094bf018ba12..a479bbc09d47 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts @@ -8,7 +8,7 @@ import { Locations, LocationStatus, PrivateLocation } from '../../../../common/runtime_types'; import { normalizeProjectMonitors } from '.'; -describe('http normalizers', () => { +describe('tcp normalizers', () => { describe('normalize push monitors', () => { const projectId = 'test-project-id'; const locations: Locations = [ @@ -83,7 +83,7 @@ describe('http normalizers', () => { }, ]; - it('properly normalizes http monitors', () => { + it('properly normalizes tcp monitors', () => { const actual = normalizeProjectMonitors({ locations, privateLocations, @@ -106,6 +106,7 @@ describe('http normalizers', () => { enabled: true, form_monitor_type: 'tcp', hosts: 'smtp.gmail.com:587', + 'url.port': null, journey_id: 'gmail-smtp', locations: [ { @@ -157,6 +158,7 @@ describe('http normalizers', () => { enabled: true, form_monitor_type: 'tcp', hosts: 'localhost:18278', + 'url.port': null, journey_id: 'always-down', locations: [ { @@ -221,6 +223,7 @@ describe('http normalizers', () => { enabled: true, form_monitor_type: 'tcp', hosts: 'localhost', + 'url.port': null, journey_id: 'always-down', locations: [ { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts index 1ca27869aa2c..16cbf3f33a8a 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts @@ -19,7 +19,6 @@ import { deleteMonitorBulk } from '../../routes/monitor_cruds/bulk_cruds/delete_ import { SyntheticsMonitorClient } from '../synthetics_monitor/synthetics_monitor_client'; import { syncEditedMonitorBulk } from '../../routes/monitor_cruds/bulk_cruds/edit_monitor_bulk'; import { - BrowserFields, ConfigKey, SyntheticsMonitorWithSecrets, EncryptedSyntheticsMonitor, @@ -124,16 +123,16 @@ export class ProjectMonitorFormatter { const existingMonitors = await this.getProjectMonitorsForProject(); this.staleMonitorsMap = await this.getStaleMonitorsMap(existingMonitors); - const normalizedNewMonitors: BrowserFields[] = []; + const normalizedNewMonitors: SyntheticsMonitor[] = []; const normalizedUpdateMonitors: Array<{ previousMonitor: SavedObjectsFindResult; - monitor: BrowserFields; + monitor: SyntheticsMonitor; }> = []; for (const monitor of this.monitors) { const previousMonitor = existingMonitors.find( (monitorObj) => - (monitorObj.attributes as BrowserFields)[ConfigKey.JOURNEY_ID] === monitor.id + (monitorObj.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID] === monitor.id ); const normM = await this.validateProjectMonitor({ @@ -154,7 +153,7 @@ export class ProjectMonitorFormatter { await this.createMonitorsBulk(normalizedNewMonitors); - const { updatedCount } = await this.updateMonitors(normalizedUpdateMonitors); + const { updatedCount } = await this.updateMonitorsBulk(normalizedUpdateMonitors); if (normalizedUpdateMonitors.length > 0) { let updateMessage = ''; @@ -228,16 +227,16 @@ export class ProjectMonitorFormatter { } /* Validates that the normalized monitor is a valid monitor saved object type */ - const { valid: isNormalizedMonitorValid } = this.validateMonitor({ + const { valid: isNormalizedMonitorValid, decodedMonitor } = this.validateMonitor({ validationResult: validateMonitor(normalizedMonitor as MonitorFields), monitorId: monitor.id, }); - if (!isNormalizedMonitorValid) { + if (!isNormalizedMonitorValid || !decodedMonitor) { return null; } - return normalizedMonitor; + return decodedMonitor; } catch (e) { this.server.logger.error(e); this.failedMonitors.push({ @@ -259,7 +258,7 @@ export class ProjectMonitorFormatter { const staleMonitors: StaleMonitorMap = {}; existingMonitors.forEach((savedObject) => { - const journeyId = (savedObject.attributes as BrowserFields)[ConfigKey.JOURNEY_ID]; + const journeyId = (savedObject.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID]; if (journeyId) { staleMonitors[journeyId] = { stale: true, @@ -291,7 +290,7 @@ export class ProjectMonitorFormatter { return hits; }; - private createMonitorsBulk = async (monitors: BrowserFields[]) => { + private createMonitorsBulk = async (monitors: SyntheticsMonitor[]) => { try { if (monitors.length > 0) { const { newMonitors } = await syncNewMonitorBulk({ @@ -352,9 +351,9 @@ export class ProjectMonitorFormatter { ); }; - private updateMonitors = async ( + private updateMonitorsBulk = async ( monitors: Array<{ - monitor: BrowserFields; + monitor: SyntheticsMonitor; previousMonitor: SavedObjectsFindResult; }> ): Promise<{ diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts index 4e65bf1c4dbe..f96479a50bc6 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor.ts @@ -140,6 +140,41 @@ export default function ({ getService }: FtrProviderContext) { expect(apiResponse.status).eql(400); }); + it('omits unknown keys', async () => { + // Delete a required property to make payload invalid + const newMonitor = { + name: 'Sample name', + url: 'https://elastic.co', + unknownKey: 'unknownValue', + type: 'http', + locations: [ + { + id: 'eu-west-01', + label: 'Europe West', + geo: { + lat: 33.2343132435, + lon: 73.2342343434, + }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + ], + }; + + const apiResponse = await supertestAPI + .post(API_URLS.SYNTHETICS_MONITORS) + .set('kbn-xsrf', 'true') + .send(newMonitor) + .expect(200); + + const response = await supertestAPI + .get(`${API_URLS.SYNTHETICS_MONITORS}/${apiResponse.body.id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(response.body.attributes).not.to.have.keys('unknownkey', 'url'); + }); + it('can create monitor with API key with proper permissions', async () => { await supertestAPI .post('/internal/security/api_key') diff --git a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts index 105e6521c1da..9dce7e7d8fda 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/add_monitor_project.ts @@ -96,6 +96,114 @@ export default function ({ getService }: FtrProviderContext) { icmpProjectMonitors = setUniqueIds(getFixtureJson('project_icmp_monitor')); }); + it('project monitors - handles browser monitors', async () => { + const successfulMonitors = [projectMonitors.monitors[0]]; + + try { + const messages = await parseStreamApiResponse( + projectMonitorEndpoint, + JSON.stringify(projectMonitors) + ); + + expect(messages).to.have.length(2); + expect(messages[1].updatedMonitors).eql([]); + expect(messages[1].createdMonitors).eql(successfulMonitors.map((monitor) => monitor.id)); + expect(messages[1].failedMonitors).eql([]); + + for (const monitor of successfulMonitors) { + const journeyId = monitor.id; + const createdMonitorsResponse = await supertest + .get(API_URLS.SYNTHETICS_MONITORS) + .query({ filter: `${syntheticsMonitorType}.attributes.journey_id: ${journeyId}` }) + .set('kbn-xsrf', 'true') + .expect(200); + + const decryptedCreatedMonitor = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(decryptedCreatedMonitor.body.attributes).to.eql({ + __ui: { + is_zip_url_tls_enabled: false, + script_source: { + file_name: '', + is_generated_script: false, + }, + }, + config_id: '', + custom_heartbeat_id: `${journeyId}-test-suite-default`, + enabled: true, + 'filter_journeys.match': 'check if title is present', + 'filter_journeys.tags': [], + form_monitor_type: 'multistep', + ignore_https_errors: false, + journey_id: journeyId, + locations: [ + { + geo: { + lat: 0, + lon: 0, + }, + id: 'localhost', + isInvalid: false, + isServiceManaged: true, + label: 'Local Synthetics Service', + status: 'experimental', + url: 'mockDevUrl', + }, + ], + name: 'check if title is present', + namespace: 'default', + origin: 'project', + original_space: 'default', + playwright_options: '{"headless":true,"chromiumSandbox":false}', + playwright_text_assertion: '', + project_id: 'test-suite', + params: '', + revision: 1, + schedule: { + number: '10', + unit: 'm', + }, + screenshots: 'on', + 'service.name': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + 'source.zip_url.url': '', + 'source.zip_url.password': '', + 'source.zip_url.username': '', + synthetics_args: [], + tags: [], + 'throttling.config': '5d/3u/20l', + 'throttling.download_speed': '5', + 'throttling.is_enabled': true, + 'throttling.latency': '20', + 'throttling.upload_speed': '3', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + 'ssl.key': '', + 'ssl.key_passphrase': '', + 'source.inline.script': '', + 'source.project.content': + 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA', + timeout: null, + type: 'browser', + 'url.port': null, + urls: '', + }); + } + } finally { + await Promise.all([ + successfulMonitors.map((monitor) => { + return deleteMonitor(monitor.id, httpProjectMonitors.project); + }), + ]); + } + }); + it('project monitors - handles http monitors', async () => { const kibanaVersion = await kibanaServer.version.get(); const successfulMonitors = [httpProjectMonitors.monitors[1]]; @@ -130,7 +238,12 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); - expect(createdMonitorsResponse.body.monitors[0].attributes).to.eql({ + const decryptedCreatedMonitor = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(decryptedCreatedMonitor.body.attributes).to.eql({ __ui: { is_tls_enabled: false, }, @@ -138,6 +251,16 @@ export default function ({ getService }: FtrProviderContext) { 'check.response.status': ['200'], config_id: '', custom_heartbeat_id: `${journeyId}-test-suite-default`, + 'check.response.body.negative': [], + 'check.response.body.positive': ['Saved', 'saved'], + 'check.response.headers': {}, + 'check.request.body': { + type: 'text', + value: '', + }, + 'check.request.headers': { + 'Content-Type': 'application/x-www-form-urlencoded', + }, enabled: false, form_monitor_type: 'http', journey_id: journeyId, @@ -161,6 +284,8 @@ export default function ({ getService }: FtrProviderContext) { origin: 'project', original_space: 'default', project_id: 'test-suite', + username: '', + password: '', proxy_url: '', 'response.include_body': 'always', 'response.include_headers': false, @@ -174,10 +299,13 @@ export default function ({ getService }: FtrProviderContext) { 'ssl.certificate_authorities': '', 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], 'ssl.verification_mode': 'full', + 'ssl.key': '', + 'ssl.key_passphrase': '', tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','), timeout: '80', type: 'http', urls: Array.isArray(monitor.urls) ? monitor.urls?.[0] : monitor.urls, + 'url.port': null, }); } } finally { @@ -223,12 +351,19 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); - expect(createdMonitorsResponse.body.monitors[0].attributes).to.eql({ + const decryptedCreatedMonitor = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(decryptedCreatedMonitor.body.attributes).to.eql({ __ui: { is_tls_enabled: false, }, config_id: '', custom_heartbeat_id: `${journeyId}-test-suite-default`, + 'check.receive': '', + 'check.send': '', enabled: true, form_monitor_type: 'tcp', journey_id: journeyId, @@ -263,10 +398,13 @@ export default function ({ getService }: FtrProviderContext) { 'ssl.certificate_authorities': '', 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], 'ssl.verification_mode': 'full', + 'ssl.key': '', + 'ssl.key_passphrase': '', tags: Array.isArray(monitor.tags) ? monitor.tags : monitor.tags?.split(','), timeout: '16', type: 'tcp', hosts: Array.isArray(monitor.hosts) ? monitor.hosts?.[0] : monitor.hosts, + 'url.port': null, }); } } finally { @@ -312,7 +450,12 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); - expect(createdMonitorsResponse.body.monitors[0].attributes).to.eql({ + const decryptedCreatedMonitor = await supertest + .get(`${API_URLS.SYNTHETICS_MONITORS}/${createdMonitorsResponse.body.monitors[0].id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(decryptedCreatedMonitor.body.attributes).to.eql({ config_id: '', custom_heartbeat_id: `${journeyId}-test-suite-default`, enabled: true, diff --git a/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts index 480d07e7144f..eb44aa36a76c 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/edit_monitor.ts @@ -16,7 +16,7 @@ import { getFixtureJson } from './helper/get_fixture_json'; import { PrivateLocationTestService } from './services/private_location_test_service'; export default function ({ getService }: FtrProviderContext) { - describe('[PUT] /internal/uptime/service/monitors', function () { + describe('EditMonitor', function () { this.tags('skipCloud'); const supertest = getService('supertest'); @@ -109,6 +109,66 @@ export default function ({ getService }: FtrProviderContext) { ); }); + it('strips unknown keys from monitor edits', async () => { + const newMonitor = httpMonitorJson; + + const { id: monitorId, attributes: savedMonitor } = await saveMonitor( + newMonitor as MonitorFields + ); + + expect(savedMonitor).eql(omit(newMonitor, secretKeys)); + + const updates: Partial = { + [ConfigKey.URLS]: 'https://modified-host.com', + [ConfigKey.NAME]: 'Modified name', + [ConfigKey.LOCATIONS]: [ + { + id: 'eu-west-01', + label: 'Europe West', + geo: { + lat: 33.2343132435, + lon: 73.2342343434, + }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + ], + [ConfigKey.REQUEST_HEADERS_CHECK]: { + sampleHeader2: 'sampleValue2', + }, + [ConfigKey.METADATA]: { + script_source: { + is_generated_script: false, + file_name: 'test-file.name', + }, + }, + unknownkey: 'unknownvalue', + } as Partial; + + const modifiedMonitor = omit( + { + ...newMonitor, + ...updates, + [ConfigKey.METADATA]: { + ...newMonitor[ConfigKey.METADATA], + ...updates[ConfigKey.METADATA], + }, + }, + ['unknownkey'] + ); + + const editResponse = await supertest + .put(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) + .set('kbn-xsrf', 'true') + .send(modifiedMonitor) + .expect(200); + + expect(editResponse.body.attributes).eql( + omit({ ...modifiedMonitor, revision: 2 }, secretKeys) + ); + expect(editResponse.body.attributes).not.to.have.keys('unknownkey'); + }); + it('returns 404 if monitor id is not present', async () => { const invalidMonitorId = 'invalid-id'; const expected404Message = `Monitor id ${invalidMonitorId} not found!`; diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json index 76478fd7aee1..45a2e11e9f30 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json @@ -23,6 +23,7 @@ "max_redirects": "3", "password": "test", "urls": "https://nextjs-test-synthetics.vercel.app/api/users", + "url.port": null, "proxy_url": "http://proxy.com", "check.response.body.negative": [], "check.response.body.positive": [], diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json index d9df06d1c7c3..052c811461ae 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json @@ -1,5 +1,5 @@ { - "type": "tcp", + "type": "icmp", "locations": [], "journey_id": "", "enabled": true, @@ -14,25 +14,8 @@ "tagT2" ], "timeout": "16", - "__ui": { - "is_tls_enabled": true, - "is_zip_url_tls_enabled": false - }, "hosts": "192.33.22.111:3333", - "proxy_url": "", - "proxy_use_local_resolver": false, - "check.receive": "", - "check.send": "", - "ssl.certificate_authorities": "", - "ssl.certificate": "", - "ssl.key": "", - "ssl.key_passphrase": "", - "ssl.verification_mode": "full", - "ssl.supported_protocols": [ - "TLSv1.1", - "TLSv1.2", - "TLSv1.3" - ], + "wait": "1", "name": "Test HTTP Monitor 04", "namespace": "testnamespace", "origin": "ui", diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json index 1c726c1bcc70..e0b4ca03b1d8 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json @@ -15,6 +15,7 @@ "is_zip_url_tls_enabled": false }, "hosts": "example-host:40", + "url.port": null, "proxy_url": "", "proxy_use_local_resolver": false, "check.receive": "", From 1be987e66a36cf693e124ed0ed1d278ffc52358b Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 29 Sep 2022 08:29:07 -0700 Subject: [PATCH 128/185] [DOCS] Link to open API specification from ML sync API (#142136) --- .../machine-learning/ml-apis.asciidoc | 2 +- .../machine-learning/ml_apis_v2_defs.asciidoc | 240 ------------------ .../machine-learning/ml_apis_v2_docs.asciidoc | 49 ---- docs/api/machine-learning/sync.asciidoc | 95 +++++++ docs/redirects.asciidoc | 6 +- docs/user/api.asciidoc | 3 +- 6 files changed, 100 insertions(+), 295 deletions(-) delete mode 100644 docs/api/machine-learning/ml_apis_v2_defs.asciidoc delete mode 100644 docs/api/machine-learning/ml_apis_v2_docs.asciidoc create mode 100644 docs/api/machine-learning/sync.asciidoc diff --git a/docs/api-generated/machine-learning/ml-apis.asciidoc b/docs/api-generated/machine-learning/ml-apis.asciidoc index 3482109d4ab3..60aff48267f0 100644 --- a/docs/api-generated/machine-learning/ml-apis.asciidoc +++ b/docs/api-generated/machine-learning/ml-apis.asciidoc @@ -1,4 +1,4 @@ -[[machine-learning-api]] +[[machine-learning-apis]] == Machine learning APIs preview::[] diff --git a/docs/api/machine-learning/ml_apis_v2_defs.asciidoc b/docs/api/machine-learning/ml_apis_v2_defs.asciidoc deleted file mode 100644 index 691557bfb963..000000000000 --- a/docs/api/machine-learning/ml_apis_v2_defs.asciidoc +++ /dev/null @@ -1,240 +0,0 @@ -[[Machine_learning_APIs-definitions]] -== Definitions - -* <> -* <> -* <> -* <> -* <> -* <> - -[[MLSyncResponse]] -=== `MLSyncResponse` - -The sync machine learning saved objects API returns this list of machine learning saved objects that required synchronization. - - -==== Properties - -`datafeedsAdded` (++map[string,++<>++]++):: -If a saved object for an anomaly detection job is missing a datafeed identifier, it is added when you run the sync machine learning saved objects API. - - -`datafeedsRemoved` (++map[string,++<>++]++):: -If a saved object for an anomaly detection job references a datafeed that no longer exists, it is deleted when you run the sync machine learning saved objects API. - - -`savedObjectsCreated` (<>):: -If saved objects are missing for machine learning jobs or trained models, they are created when you run the sync machine learning saved objects API. - - -`savedObjectsDeleted` (<>):: -If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted when you run the sync machine learning saved objects API. - - -==== Example - -[source,json] --------- -{ - "datafeedsAdded" : { - "some_property" : { - "success" : true - } - }, - "datafeedsRemoved" : { - "some_property" : { - "success" : true - } - }, - "savedObjectsCreated" : { - "anomaly-detector" : { - "some_property" : { - "success" : true - } - }, - "data-frame-analytics" : { - "some_property" : { - "success" : true - } - }, - "trained-model" : { - "some_property" : { - "success" : true - } - } - }, - "savedObjectsDeleted" : { - "anomaly-detector" : { - "some_property" : { - "success" : true - } - }, - "data-frame-analytics" : { - "some_property" : { - "success" : true - } - }, - "trained-model" : { - "some_property" : { - "success" : true - } - } - } -} - --------- - -[[MLSyncResponse-Datafeeds]] -=== `MLSyncResponse-Datafeeds` - -The sync machine learning saved objects API response contains this object when there are datafeeds affected by the synchronization. There is an object for each relevant datafeed, which contains the synchronization status. - - -==== Properties - -`success` (+boolean+):: -The success or failure of the synchronization. - - -==== Example - -[source,json] --------- -{ - "success" : true -} - --------- - -[[MLSyncResponse-Jobs]] -=== `MLSyncResponse-Jobs` - -The sync machine learning saved objects API response contains this object when there are machine learning jobs affected by the synchronization. There is an object for each relevant job, which contains the synchronization status. - - -==== Properties - -`success` (+boolean+):: -The success or failure of the synchronization. - - -==== Example - -[source,json] --------- -{ - "success" : true -} - --------- - -[[MLSyncResponse-Models]] -=== `MLSyncResponse-Models` - -The sync machine learning saved objects API response contains this object when there are trained models affected by the synchronization. There is an object for each relevant trained model, which contains the synchronization status. - - -==== Properties - -`success` (+boolean+):: -The success or failure of the synchronization. - - -==== Example - -[source,json] --------- -{ - "success" : true -} - --------- - -[[MLSyncResponse-SavedObjectsCreated]] -=== `MLSyncResponse-SavedObjectsCreated` - -If saved objects are missing for machine learning jobs or trained models, they are created when you run the sync machine learning saved objects API. - - -==== Properties - -`anomaly-detector` (++map[string,++<>++]++):: -This object is present if there are anomaly detection jobs affected by the synchronization. - - -`data-frame-analytics` (++map[string,++<>++]++):: -This object is present if there are data frame analytics jobs affected by the synchronization. - - -`trained-model` (++map[string,++<>++]++):: -This object is present if there are trained models affected by the synchronization. - - -==== Example - -[source,json] --------- -{ - "anomaly-detector" : { - "some_property" : { - "success" : true - } - }, - "data-frame-analytics" : { - "some_property" : { - "success" : true - } - }, - "trained-model" : { - "some_property" : { - "success" : true - } - } -} - --------- - -[[MLSyncResponse-SavedObjectsDeleted]] -=== `MLSyncResponse-SavedObjectsDeleted` - -If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted when you run the sync machine learning saved objects API. - - -==== Properties - -`anomaly-detector` (++map[string,++<>++]++):: -This object is present if there are anomaly detection jobs affected by the synchronization. - - -`data-frame-analytics` (++map[string,++<>++]++):: -This object is present if there are data frame analytics jobs affected by the synchronization. - - -`trained-model` (++map[string,++<>++]++):: -This object is present if there are trained models affected by the synchronization. - - -==== Example - -[source,json] --------- -{ - "anomaly-detector" : { - "some_property" : { - "success" : true - } - }, - "data-frame-analytics" : { - "some_property" : { - "success" : true - } - }, - "trained-model" : { - "some_property" : { - "success" : true - } - } -} - --------- diff --git a/docs/api/machine-learning/ml_apis_v2_docs.asciidoc b/docs/api/machine-learning/ml_apis_v2_docs.asciidoc deleted file mode 100644 index f4a01cad9420..000000000000 --- a/docs/api/machine-learning/ml_apis_v2_docs.asciidoc +++ /dev/null @@ -1,49 +0,0 @@ -[[Machine_learning_APIs]] -== Machine learning APIs - -* <> - -[[ml-sync]] -=== Sync machine learning objects - -Synchronizes Kibana saved objects for machine learning jobs and trained models. You must have `all` privileges for the *Machine Learning* feature in the *Analytics* section of the Kibana feature privileges. This API runs automatically when you start Kibana and periodically thereafter. - - -==== Request - -`GET /s/{spaceId}/api/ml/saved_objects/sync` - -==== Path parameters - -[options="header"] -|========== -|Name |Type |Required |Description -|`spaceId` |+string+ |Y |An identifier for the space. If you omit `/s/` and this identifier from the path, the default space is used. - -|========== -==== Query parameters - -[options="header"] -|========== -|Name |Type |Required |Description -|`simulate` |+boolean+; default: ++false++ |N |When true, simulates the synchronization by returning only the list actions that would be performed. - -|========== -==== Responses - -`200`:: -+ --- -(<>) - -Indicates a successful call. - --- - -==== Request example - -[source,json] --------- -curl -XGET https://localhost:5601/s/{spaceId}/api/ml/saved_objects/sync \ --u USER:PASSWORD --------- \ No newline at end of file diff --git a/docs/api/machine-learning/sync.asciidoc b/docs/api/machine-learning/sync.asciidoc new file mode 100644 index 000000000000..af4f797ade1f --- /dev/null +++ b/docs/api/machine-learning/sync.asciidoc @@ -0,0 +1,95 @@ +[[machine-learning-api-sync]] +=== Sync {ml} saved objects API +++++ +Sync {ml} saved objects +++++ + +Synchronizes {kib} saved objects for {ml} jobs and trained models. + +[NOTE] +==== +For the most up-to-date API details, refer to the +{kib-repo}/tree/{branch}/x-pack/plugins/ml/common/openapi[open API specification]. For a preview, check out <>. +==== + +[[machine-learning-api-sync-request]] +==== {api-request-title} + +`GET :/api/ml/saved_objects/sync` + +`GET :/s//api/ml/saved_objects/sync` + +[[machine-learning-api-sync-prereq]] +==== {api-prereq-title} + +You must have `all` privileges for the *Machine Learning* feature in the *Analytics* section of the +<>. + +[[machine-learning-api-sync-desc]] +==== {api-description-title} + +This API runs automatically when you start {kib} and periodically thereafter. + +[[machine-learning-api-sync-path-params]] +==== {api-path-parms-title} + +`space_id`:: +(Optional, string) An identifier for the space. If `space_id` is not provided in +the URL the default space is used. + +[[machine-learning-api-sync-query-params]] +==== {api-query-parms-title} + +`simulate`:: +(Optional, boolean) When `true`, simulates the synchronization by only returning +the list actions that _would_ be performed. + +[[machine-learning-api-sync-response-body]] +==== {api-response-body-title} + +`datafeedsAdded`:: +(array) If a saved object for an {anomaly-job} is missing a {dfeed} identifier, +it is added. This list contains the {dfeed} identifiers and indicates whether +the synchronization was successful. + +`datafeedsRemoved`:: +(array) If a saved object for an anomaly detection job references a datafeed +that no longer exists, it is deleted. This list contains the {dfeed} identifiers +and indicates whether the synchronization was successful. + +`savedObjectsCreated`:: +(array) If saved objects are missing for {ml} jobs or trained models, they are +created. This list contains the job and model identifiers and indicates whether +the synchronization was successful. + +`savedObjectsDeleted`:: +(array) If saved objects exist for {ml} jobs or trained models that no longer +exist, they are deleted. This list contains the job and model identifiers and +indicates whether the synchronization was successful. + +[[machine-learning-api-sync-codes]] +==== {api-response-codes-title} + +`200`:: + Indicates a successful call. + +[[machine-learning-api-sync-example]] +==== {api-examples-title} + +Retrieve the list of {ml} saved objects that require synchronization: + +[source,sh] +-------------------------------------------------- +GET api/ml/saved_objects/sync?simulate=true +-------------------------------------------------- +// KIBANA + +If there are two jobs that need to be synchronized, for example, the API returns +the following response: + +[source,sh] +-------------------------------------------------- +{"savedObjectsCreated":{"anomaly_detector":{"myjob1":{"success":true},"myjob2":{"success":true}}},"savedObjectsDeleted":{},"datafeedsAdded":{},"datafeedsRemoved":{}} +-------------------------------------------------- + +To perform the synchronization, re-run the API and omit the `simulate` parameter. \ No newline at end of file diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 5c12048315de..fe1f3b9521cf 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -416,7 +416,7 @@ This page has been deleted. Refer to <>. This page has been deleted. Refer to <>. -[role="exclude",id="machine-learning-api-sync"] -== Sync machine learning saved objects API +[role="exclude",id="ml-sync"] +== Sync machine learning objects API -This page has been deleted. Refer to <>. +This page has been deleted. Refer to <>. \ No newline at end of file diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index 8f55645e5b91..aa567487b296 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -100,8 +100,7 @@ include::{kib-repo-dir}/api/actions-and-connectors.asciidoc[] include::{kib-repo-dir}/api/cases.asciidoc[] include::{kib-repo-dir}/api/dashboard-api.asciidoc[] include::{kib-repo-dir}/api/logstash-configuration-management.asciidoc[] -include::{kib-repo-dir}/api/machine-learning/ml_apis_v2_docs.asciidoc[] -include::{kib-repo-dir}/api/machine-learning/ml_apis_v2_defs.asciidoc[leveloffset=+1] +include::{kib-repo-dir}/api/machine-learning.asciidoc[] include::{kib-repo-dir}/api/osquery-manager.asciidoc[] include::{kib-repo-dir}/api/short-urls.asciidoc[] include::{kib-repo-dir}/api/task-manager/health.asciidoc[] From d20b96f8a7a14a71584476d21a639929a4b499ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Thu, 29 Sep 2022 17:34:53 +0200 Subject: [PATCH 129/185] [DOCS] Adds log pattern analysis docs to AIOps Labs subsection (#141939) --- .../ml/images/ml-log-pattern-analysis.png | Bin 0 -> 174028 bytes docs/user/ml/index.asciidoc | 28 ++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 docs/user/ml/images/ml-log-pattern-analysis.png diff --git a/docs/user/ml/images/ml-log-pattern-analysis.png b/docs/user/ml/images/ml-log-pattern-analysis.png new file mode 100644 index 0000000000000000000000000000000000000000..0cf16105a11e9df0d418c733244174b6065f53bc GIT binary patch literal 174028 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

    2V!Y0|JSdt|>A&W`h^QP@ax;D@Wd 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 literal 0 HcmV?d00001 diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index c58408d85d37..7467d99ca22e 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -118,8 +118,8 @@ Examine the histogram chart of the log rates for a given {data-source}, and find the reason behind a particular change possibly in millions of log events across multiple fields and values. -You can find explain log rate spikes under **{ml-app}** > **AIOps** where you -can select the {data-source} or saved search that you want to analyze. +You can find explain log rate spikes under **{ml-app}** > **AIOps Labs** where +you can select the {data-source} or saved search that you want to analyze. [role="screenshot"] image::user/ml/images/ml-explain-log-rate-before.png[Log event histogram chart] @@ -142,3 +142,27 @@ deviation and rerun the analysis with the modified values. [role="screenshot"] image::user/ml/images/ml-explain-log-rate.png[Log rate spike explained] + +[discrete] +[[log-pattern-analysis]] +=== Log pattern analysis + +preview::[] + +Log pattern analysis helps you to find patterns in unstructured log messages and +makes it easier to examine your data. It performs categorization analysis on a +selected field of a {data-source}, creates categories based on the data and +displays them together with a chart that shows the distribution of each category +and an example document that matches the category. + +You can find log pattern analysis under **{ml-app}** > **AIOps Labs** where you +can select the {data-source} or saved search that you want to analyze. + +[role="screenshot"] +image::user/ml/images/ml-log-pattern-analysis.png[Log pattern analysis UI] + +Select a field for categorization and optionally apply any filters that you +want, then start the analysis. The analysis uses the same algorithms as a {ml} +categorization job. The results of the analysis are shown in a table that makes +it possible to open **Discover** and show or filter out the given category +there, which helps you to further examine your log messages. \ No newline at end of file From 17089a2808a94e5d11077b76fc95480b4990a705 Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Thu, 29 Sep 2022 10:40:03 -0500 Subject: [PATCH 130/185] [TIP] FilterIn FilterOut component cleanup (#141525) - rework FilterIn and FilterOut components and extract logic to hook - rework InvestigateInTimeline components and extract logic to hook - rework AddToTimeline components and extract logic to hook - rename DataGrid components to CellAction - replace prop as by Component to stay consistent with Eui library - remove all ...props usage and replace with ['data-test-subj']: dataTestSubj - add missing translations --- .../common/types/component_type.ts | 14 - .../mocks/mock_indicators_filters_context.tsx | 2 +- .../flyout/fields_table/fields_table.tsx | 19 +- .../indicators/components/flyout/flyout.tsx | 2 +- .../indicator_value_actions.tsx | 33 +- .../flyout/overview_tab/block/block.tsx | 14 +- .../highlighted_values_table.tsx | 4 +- .../indicator_barchart_legend_action.tsx | 30 +- .../indicators_table/actions_row_cell.tsx | 2 +- .../indicators_table/cell_actions.tsx | 21 +- .../__snapshots__/filter_in.test.tsx.snap | 18 +- .../filter_in/filter_in.stories.tsx | 55 ++- .../components/filter_in/filter_in.test.tsx | 39 +-- .../components/filter_in/filter_in.tsx | 151 +++++---- .../__snapshots__/filter_out.test.tsx.snap | 18 +- .../filter_out/filter_out.stories.tsx | 55 ++- .../components/filter_out/filter_out.test.tsx | 40 +-- .../components/filter_out/filter_out.tsx | 151 +++++---- .../query_bar/hooks/use_filter_in_out.test.ts | 56 ++++ .../query_bar/hooks/use_filter_in_out.ts | 66 ++++ .../add_to_timeline.stories.tsx | 22 +- .../add_to_timeline/add_to_timeline.test.tsx | 14 +- .../add_to_timeline/add_to_timeline.tsx | 144 +++++--- .../investigate_in_timeline.test.tsx.snap | 313 ++++++++++++++++++ .../index.ts | 2 +- .../investigate_in_timeline.stories.tsx} | 15 +- .../investigate_in_timeline.test.tsx | 77 +++++ .../investigate_in_timeline.tsx | 90 +++++ ...vestigate_in_timeline_button.test.tsx.snap | 155 --------- ...investigate_in_timeline_button.stories.tsx | 27 -- .../investigate_in_timeline_button.test.tsx | 45 --- .../investigate_in_timeline_button.tsx | 50 --- ...gate_in_timeline_button_icon.test.tsx.snap | 159 --------- .../index.ts | 8 - ...vestigate_in_timeline_button_icon.test.tsx | 46 --- .../investigate_in_timeline_button_icon.tsx | 62 ---- .../public/modules/timeline/hooks/index.ts | 1 + .../hooks/use_add_to_timeline.test.tsx | 62 ++++ .../timeline/hooks/use_add_to_timeline.ts | 61 ++++ .../use_investigate_in_timeline.test.tsx | 9 +- .../hooks/use_investigate_in_timeline.ts | 9 +- 41 files changed, 1262 insertions(+), 899 deletions(-) delete mode 100644 x-pack/plugins/threat_intelligence/common/types/component_type.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/__snapshots__/investigate_in_timeline.test.tsx.snap rename x-pack/plugins/threat_intelligence/public/modules/timeline/components/{investigate_in_timeline_button => investigate_in_timeline}/index.ts (83%) rename x-pack/plugins/threat_intelligence/public/modules/timeline/components/{investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.stories.tsx => investigate_in_timeline/investigate_in_timeline.stories.tsx} (67%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.test.tsx create mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/__snapshots__/investigate_in_timeline_button.test.tsx.snap delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.stories.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.test.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/__snapshots__/investigate_in_timeline_button_icon.test.tsx.snap delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/index.ts delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.test.tsx delete mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.tsx create mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.test.tsx create mode 100644 x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.ts diff --git a/x-pack/plugins/threat_intelligence/common/types/component_type.ts b/x-pack/plugins/threat_intelligence/common/types/component_type.ts deleted file mode 100644 index d5982b15c351..000000000000 --- a/x-pack/plugins/threat_intelligence/common/types/component_type.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. - */ - -/** - * Used in multiple component to drive the render of the component depending on where they're used. - */ -export enum ComponentType { - EuiDataGrid = 'EuiDataGrid', - ContextMenu = 'ContextMenu', -} diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx index 4b7832631fd1..baad954a4466 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_indicators_filters_context.tsx @@ -11,7 +11,7 @@ import { IndicatorsFiltersContextValue } from '../../modules/indicators/containe export const mockIndicatorsFiltersContext: IndicatorsFiltersContextValue = { filterManager: { getFilters: () => [], - setFilters: () => {}, + setFilters: () => window.alert('setFilters'), } as unknown as FilterManager, filters: [], filterQuery: { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx index e670d6b90e02..eb5b2d0ca258 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/fields_table/fields_table.tsx @@ -22,7 +22,7 @@ export interface IndicatorFieldsTableProps { export const IndicatorFieldsTable: VFC = ({ fields, indicator, - ...rest + 'data-test-subj': dataTestSubj, }) => { const columns = useMemo( () => @@ -49,15 +49,26 @@ export const IndicatorFieldsTable: VFC = ({ actions: [ { render: (field: string) => ( - + ), width: '72px', }, ], }, ] as Array>, - [indicator, rest] + [indicator, dataTestSubj] ); - return ; + return ( + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx index 24fe1cc0082e..11102df79701 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/flyout.tsx @@ -21,7 +21,7 @@ import { useGeneratedHtmlId, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { InvestigateInTimelineButton } from '../../../timeline/components/investigate_in_timeline_button'; +import { InvestigateInTimelineButton } from '../../../timeline/components/investigate_in_timeline'; import { DateFormatter } from '../../../../components/date_formatter/date_formatter'; import { Indicator, RawIndicatorFieldId } from '../../../../../common/types/indicator'; import { IndicatorsFlyoutJson } from './json_tab'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx index 919b39da28c3..0ee9ae050aa9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/indicator_value_actions/indicator_value_actions.tsx @@ -5,13 +5,12 @@ * 2.0. */ -import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; import React, { VFC } from 'react'; +import { EuiFlexGroup } from '@elastic/eui'; import { Indicator } from '../../../../../../common/types/indicator'; -import { FilterIn } from '../../../../query_bar/components/filter_in'; -import { FilterOut } from '../../../../query_bar/components/filter_out'; -import { AddToTimeline } from '../../../../timeline/components/add_to_timeline'; +import { FilterInButtonIcon } from '../../../../query_bar/components/filter_in'; +import { FilterOutButtonIcon } from '../../../../query_bar/components/filter_out'; +import { AddToTimelineButtonIcon } from '../../../../timeline/components/add_to_timeline'; import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../../utils/field_value'; export const TIMELINE_BUTTON_TEST_ID = 'TimelineButton'; @@ -27,10 +26,6 @@ interface IndicatorValueActions { * Indicator field used for the filter in/out and add to timeline feature. */ field: string; - /** - * Only used with `EuiDataGrid` (see {@link AddToTimelineButtonProps}). - */ - Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; /** * Used for unit and e2e tests. */ @@ -40,28 +35,22 @@ interface IndicatorValueActions { export const IndicatorValueActions: VFC = ({ indicator, field, - Component, - ...props + 'data-test-subj': dataTestSubj, }) => { const { key, value } = getIndicatorFieldAndValue(indicator, field); if (!fieldAndValueValid(key, value)) { return null; } - const filterInTestId = `${props['data-test-subj']}${FILTER_IN_BUTTON_TEST_ID}`; - const filterOutTestId = `${props['data-test-subj']}${FILTER_OUT_BUTTON_TEST_ID}`; - const timelineTestId = `${props['data-test-subj']}${TIMELINE_BUTTON_TEST_ID}`; + const filterInTestId = `${dataTestSubj}${FILTER_IN_BUTTON_TEST_ID}`; + const filterOutTestId = `${dataTestSubj}${FILTER_OUT_BUTTON_TEST_ID}`; + const timelineTestId = `${dataTestSubj}${TIMELINE_BUTTON_TEST_ID}`; return ( - - - + + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx index dd8d4335feca..0866edde505b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/block/block.tsx @@ -50,10 +50,14 @@ export interface IndicatorBlockProps { /** * Renders indicator field value in a rectangle, to highlight it even more */ -export const IndicatorBlock: VFC = ({ field, indicator, ...props }) => { +export const IndicatorBlock: VFC = ({ + field, + indicator, + 'data-test-subj': dataTestSubj, +}) => { return ( - + @@ -61,7 +65,11 @@ export const IndicatorBlock: VFC = ({ field, indicator, ... - + diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx index 6ce9c332d632..7ccbbdf2f1c9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/flyout/overview_tab/highlighted_values_table/highlighted_values_table.tsx @@ -34,7 +34,7 @@ interface HighlightedValuesTableProps { */ export const HighlightedValuesTable: VFC = ({ indicator, - ...props + 'data-test-subj': dataTestSubj, }) => { const indicatorType = unwrapValue(indicator, RawIndicatorFieldId.Type); @@ -48,7 +48,7 @@ export const HighlightedValuesTable: VFC = ({ search={false} indicator={indicator} fields={highlightedFields} - {...props} + data-test-subj={dataTestSubj} /> ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx index c87f812da76f..fa573e981f6c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx @@ -8,10 +8,9 @@ import React, { useState, VFC } from 'react'; import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ComponentType } from '../../../../../common/types/component_type'; -import { FilterIn } from '../../../query_bar/components/filter_in'; -import { FilterOut } from '../../../query_bar/components/filter_out'; -import { AddToTimeline } from '../../../timeline/components/add_to_timeline'; +import { FilterInContextMenu } from '../../../query_bar/components/filter_in'; +import { FilterOutContextMenu } from '../../../query_bar/components/filter_out'; +import { AddToTimelineContextMenu } from '../../../timeline/components/add_to_timeline'; export const POPOVER_BUTTON_TEST_ID = 'tiBarchartPopoverButton'; export const TIMELINE_BUTTON_TEST_ID = 'tiBarchartTimelineButton'; @@ -28,7 +27,7 @@ export interface IndicatorBarchartLegendActionProps { */ data: string; /** - * Indicator field selected in the IndicatorFieldSelector component, passed to the {@link AddToTimeline} to populate the timeline. + * Indicator field selected in the IndicatorFieldSelector component, passed to the {@link AddToTimelineContextMenu} to populate the timeline. */ field: string; } @@ -40,24 +39,9 @@ export const IndicatorBarchartLegendAction: VFC, - , - , + , + , + , ]; return ( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/actions_row_cell.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/actions_row_cell.tsx index 1744bf8ac06c..2f84b14db0c3 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/actions_row_cell.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/actions_row_cell.tsx @@ -7,7 +7,7 @@ import React, { useContext, VFC } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; -import { InvestigateInTimelineButtonIcon } from '../../../timeline/components/investigate_in_timeline_button_icon'; +import { InvestigateInTimelineButtonIcon } from '../../../timeline/components/investigate_in_timeline'; import { Indicator } from '../../../../../common/types/indicator'; import { OpenIndicatorFlyoutButton } from '../open_indicator_flyout_button/open_indicator_flyout_button'; import { IndicatorsTableContext } from './context'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx index d10ba709bfa2..9cca631ac3b5 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/cell_actions.tsx @@ -7,12 +7,11 @@ import React, { VFC } from 'react'; import { EuiDataGridColumnCellActionProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; -import { ComponentType } from '../../../../../common/types/component_type'; import { Indicator } from '../../../../../common/types/indicator'; -import { AddToTimeline } from '../../../timeline/components/add_to_timeline'; +import { AddToTimelineCellAction } from '../../../timeline/components/add_to_timeline'; +import { FilterInCellAction } from '../../../query_bar/components/filter_in'; +import { FilterOutCellAction } from '../../../query_bar/components/filter_out'; import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../utils/field_value'; -import { FilterIn } from '../../../query_bar/components/filter_in'; -import { FilterOut } from '../../../query_bar/components/filter_out'; import type { Pagination } from '../../services/fetch_indicators'; export const CELL_TIMELINE_BUTTON_TEST_ID = 'tiIndicatorsTableCellTimelineButton'; @@ -52,24 +51,22 @@ export const CellActions: VFC = ({ return ( <> - - - diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap index 34e37aaf1dd4..b9a31b09a40e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/__snapshots__/filter_in.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should render an empty component (wrong data input) 1`] = ` +exports[` should render an empty component (wrong data input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -61,7 +61,7 @@ Object { } `; -exports[` should render an empty component (wrong field input) 1`] = ` +exports[` should render an empty component (wrong field input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -122,7 +122,7 @@ Object { } `; -exports[` should render one Component (for EuiDataGrid use) 1`] = ` +exports[` should render one Component (for EuiDataGrid use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -223,7 +223,7 @@ Object { } `; -exports[` should render one EuiButtonIcon 1`] = ` +exports[` should render one EuiButtonIcon 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -320,7 +320,7 @@ Object { } `; -exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` +exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -340,7 +340,9 @@ Object { - Filter In + + Filter In + @@ -362,7 +364,9 @@ Object { - Filter In + + Filter In + diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx index 08297774e51f..d32adf70ee10 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.stories.tsx @@ -7,23 +7,70 @@ import React from 'react'; import { Story } from '@storybook/react'; +import { EuiContextMenuPanel, EuiDataGrid, EuiDataGridColumn } from '@elastic/eui'; +import { EuiDataGridColumnVisibility } from '@elastic/eui/src/components/datagrid/data_grid_types'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; +import { FilterInButtonIcon, FilterInContextMenu, FilterInCellAction } from '.'; import { IndicatorsFiltersContext } from '../../../indicators/containers/indicators_filters/context'; -import { FilterIn } from '.'; export default { - component: FilterIn, title: 'FilterIn', }; -export const Default: Story = () => { +export const ButtonIcon: Story = () => { const mockIndicator: Indicator = generateMockIndicator(); const mockField: string = 'threat.feed.name'; return ( - + + + ); +}; + +export const ContextMenu: Story = () => { + const mockIndicator: Indicator = generateMockIndicator(); + const mockField: string = 'threat.feed.name'; + const items = []; + + return ( + + + + ); +}; + +export const DataGrid: Story = () => { + const mockIndicator: Indicator = generateMockIndicator(); + const mockField: string = 'threat.feed.name'; + const columnId: string = '1'; + const columns: EuiDataGridColumn[] = [ + { + id: columnId, + cellActions: [ + ({ Component }) => ( + + ), + ], + }, + ]; + const columnVisibility: EuiDataGridColumnVisibility = { + visibleColumns: [columnId], + setVisibleColumns: () => window.alert('setVisibleColumns'), + }; + const rowCount: number = 1; + const renderCellValue = () => <>; + + return ( + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx index 788ac9b2e442..0abfa0652230 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.test.tsx @@ -11,8 +11,7 @@ import { EuiButtonIcon } from '@elastic/eui'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; -import { FilterIn } from '.'; -import { ComponentType } from '../../../../../common/types/component_type'; +import { FilterInButtonIcon, FilterInContextMenu, FilterInCellAction } from '.'; jest.mock('../../../indicators/hooks/use_indicators_filters_context'); @@ -22,48 +21,46 @@ const mockField: string = 'threat.feed.name'; const mockTestId: string = 'abc'; -describe('', () => { +describe(' ', () => { beforeEach(() => { ( useIndicatorsFiltersContext as jest.MockedFunction ).mockReturnValue(mockIndicatorsFiltersContext); }); - it('should render one EuiButtonIcon', () => { - const component = render( - - ); + it('should render an empty component (wrong data input)', () => { + const component = render(); - expect(component.getByTestId(mockTestId)).toBeInTheDocument(); expect(component).toMatchSnapshot(); }); - it('should render one Component (for EuiDataGrid use)', () => { - const mockType: ComponentType = ComponentType.EuiDataGrid; - const mockComponent: FunctionComponent = () => ; + it('should render an empty component (wrong field input)', () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + it('should render one EuiButtonIcon', () => { const component = render( - + ); + expect(component.getByTestId(mockTestId)).toBeInTheDocument(); expect(component).toMatchSnapshot(); }); it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { - const mockType: ComponentType = ComponentType.ContextMenu; - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); - it('should render an empty component (wrong data input)', () => { - const component = render(); - - expect(component).toMatchSnapshot(); - }); + it('should render one Component (for EuiDataGrid use)', () => { + const mockComponent: FunctionComponent = () => ; - it('should render an empty component (wrong field input)', () => { - const component = render(); + const component = render( + + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx index d6d62b3e3538..af6b608c1fb9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in/filter_in.tsx @@ -5,22 +5,20 @@ * 2.0. */ -import React, { useCallback, VFC } from 'react'; +import React, { VFC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiButtonIcon, EuiContextMenuItem, EuiToolTip } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; -import { ComponentType } from '../../../../../common/types/component_type'; -import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; -import { - fieldAndValueValid, - getIndicatorFieldAndValue, -} from '../../../indicators/utils/field_value'; -import { FilterIn as FilterInConst, updateFiltersArray } from '../../utils/filter'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useFilterInOut } from '../../hooks/use_filter_in_out'; +import { FilterIn } from '../../utils/filter'; import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; const ICON_TYPE = 'plusInCircle'; -const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterInButton', { +const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterInButtonIcon', { + defaultMessage: 'Filter In', +}); +const CELL_ACTION_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterInCellAction', { defaultMessage: 'Filter In', }); @@ -33,70 +31,36 @@ export interface FilterInProps { * Value used to filter in /out in the KQL bar. */ field: string; - /** - * Dictates the way the FilterIn component is rendered depending on the situation in which it's used - */ - type?: ComponentType; - /** - * Display component for when the FilterIn component is used within a DataGrid - */ - as?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem; /** * Used for unit and e2e tests. */ ['data-test-subj']?: string; } +export interface FilterInCellActionProps extends FilterInProps { + /** + * Display component for when the FilterIn component is used within an {@link EuiDataGrid}. + */ + Component: typeof EuiButtonEmpty | typeof EuiButtonIcon; +} + /** * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. * - * The component has 3 renders depending on where it's used: within a EuiContextMenu, a EuiDataGrid or not. + * This component renders an {@link EuiButtonIcon}. * - * @returns filter in button + * @returns filter in button icon */ -export const FilterIn: VFC = ({ data, field, type, as: Component, ...props }) => { - const styles = useStyles(); - - const { filterManager } = useIndicatorsFiltersContext(); - - const { key, value } = - typeof data === 'string' ? { key: field, value: data } : getIndicatorFieldAndValue(data, field); - - const filterIn = useCallback((): void => { - const existingFilters = filterManager.getFilters(); - const newFilters: Filter[] = updateFiltersArray(existingFilters, key, value, FilterInConst); - filterManager.setFilters(newFilters); - }, [filterManager, key, value]); - - if (!fieldAndValueValid(key, value)) { +export const FilterInButtonIcon: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterIn }); + if (!filterFn) { return <>; } - if (type === ComponentType.EuiDataGrid) { - return ( - -

    - {/* @ts-ignore*/} - -
    -
    - ); - } - - if (type === ComponentType.ContextMenu) { - return ( - - Filter In - - ); - } - return ( = ({ data, field, type, as: Component, iconSize="s" size="xs" color="primary" - onClick={filterIn} - {...props} + onClick={filterFn} + data-test-subj={dataTestSub} /> ); }; + +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component is to be used in an EuiContextMenu. + * + * @returns filter in item for a context menu + */ +export const FilterInContextMenu: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterIn }); + if (!filterFn) { + return <>; + } + + return ( + + + + ); +}; + +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component is to be used in an EuiDataGrid. + * + * @returns filter in button for data grid + */ +export const FilterInCellAction: VFC = ({ + data, + field, + Component, + 'data-test-subj': dataTestSub, +}) => { + const styles = useStyles(); + + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterIn }); + if (!filterFn) { + return <>; + } + + return ( + +
    + {/* @ts-ignore*/} + +
    +
    + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap index bfee4c6363ba..686e543636d0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/__snapshots__/filter_out.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should render an empty component (wrong data input) 1`] = ` +exports[` should render an empty component (wrong data input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -61,7 +61,7 @@ Object { } `; -exports[` should render an empty component (wrong field input) 1`] = ` +exports[` should render an empty component (wrong field input) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -122,7 +122,7 @@ Object { } `; -exports[` should render one Component (for EuiDataGrid use) 1`] = ` +exports[` should render one Component (for EuiDataGrid use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -223,7 +223,7 @@ Object { } `; -exports[` should render one EuiButtonIcon 1`] = ` +exports[` should render one EuiButtonIcon 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -320,7 +320,7 @@ Object { } `; -exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` +exports[` should render one EuiContextMenuItem (for EuiContextMenu use) 1`] = ` Object { "asFragment": [Function], "baseElement": @@ -340,7 +340,9 @@ Object { - Filter Out + + Filter Out + @@ -362,7 +364,9 @@ Object { - Filter Out + + Filter Out + diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx index cf625c23754a..463b249998eb 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.stories.tsx @@ -7,23 +7,70 @@ import React from 'react'; import { Story } from '@storybook/react'; +import { EuiContextMenuPanel, EuiDataGrid, EuiDataGridColumn } from '@elastic/eui'; +import { EuiDataGridColumnVisibility } from '@elastic/eui/src/components/datagrid/data_grid_types'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; +import { FilterOutButtonIcon, FilterOutContextMenu, FilterOutCellAction } from '.'; import { IndicatorsFiltersContext } from '../../../indicators/containers/indicators_filters/context'; -import { FilterOut } from '.'; export default { - component: FilterOut, title: 'FilterOut', }; -export const Default: Story = () => { +export const ButtonIcon: Story = () => { const mockIndicator: Indicator = generateMockIndicator(); const mockField: string = 'threat.feed.name'; return ( - + + + ); +}; + +export const ContextMenu: Story = () => { + const mockIndicator: Indicator = generateMockIndicator(); + const mockField: string = 'threat.feed.name'; + const items = []; + + return ( + + + + ); +}; + +export const DataGrid: Story = () => { + const mockIndicator: Indicator = generateMockIndicator(); + const mockField: string = 'threat.feed.name'; + const columnId: string = '1'; + const columns: EuiDataGridColumn[] = [ + { + id: columnId, + cellActions: [ + ({ Component }) => ( + + ), + ], + }, + ]; + const columnVisibility: EuiDataGridColumnVisibility = { + visibleColumns: [columnId], + setVisibleColumns: () => window.alert('setVisibleColumns'), + }; + const rowCount: number = 1; + const renderCellValue = () => <>; + + return ( + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx index 6a65ff503692..fa25a095191d 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.test.tsx @@ -11,8 +11,7 @@ import { EuiButtonIcon } from '@elastic/eui'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; import { mockIndicatorsFiltersContext } from '../../../../common/mocks/mock_indicators_filters_context'; -import { FilterOut } from '.'; -import { ComponentType } from '../../../../../common/types/component_type'; +import { FilterOutButtonIcon, FilterOutContextMenu, FilterOutCellAction } from '.'; jest.mock('../../../indicators/hooks/use_indicators_filters_context'); @@ -22,49 +21,46 @@ const mockField: string = 'threat.feed.name'; const mockTestId: string = 'abc'; -describe('', () => { +describe(' ', () => { beforeEach(() => { ( useIndicatorsFiltersContext as jest.MockedFunction ).mockReturnValue(mockIndicatorsFiltersContext); }); - it('should render one EuiButtonIcon', () => { - const component = render( - - ); + it('should render an empty component (wrong data input)', () => { + const component = render(); - expect(component.getByTestId(mockTestId)).toBeInTheDocument(); expect(component).toMatchSnapshot(); }); - it('should render one Component (for EuiDataGrid use)', () => { - const mockType: ComponentType = ComponentType.EuiDataGrid; - const mockComponent: FunctionComponent = () => ; + it('should render an empty component (wrong field input)', () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + it('should render one EuiButtonIcon', () => { const component = render( - + ); + expect(component.getByTestId(mockTestId)).toBeInTheDocument(); expect(component).toMatchSnapshot(); }); it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { - const mockType: ComponentType = ComponentType.ContextMenu; - - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); - it('should render an empty component (wrong data input)', () => { - const component = render(); - - expect(component).toMatchSnapshot(); - }); + it('should render one Component (for EuiDataGrid use)', () => { + const mockComponent: FunctionComponent = () => ; - it('should render an empty component (wrong field input)', () => { - const component = render(); + const component = render( + + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx index afa1bc02a6ba..3f77c14285f9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out/filter_out.tsx @@ -5,22 +5,20 @@ * 2.0. */ -import React, { useCallback, VFC } from 'react'; +import React, { VFC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiButtonIcon, EuiContextMenuItem, EuiToolTip } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; -import { ComponentType } from '../../../../../common/types/component_type'; -import { useIndicatorsFiltersContext } from '../../../indicators/hooks/use_indicators_filters_context'; -import { - fieldAndValueValid, - getIndicatorFieldAndValue, -} from '../../../indicators/utils/field_value'; -import { FilterOut as FilterOutConst, updateFiltersArray } from '../../utils/filter'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useFilterInOut } from '../../hooks/use_filter_in_out'; +import { FilterOut } from '../../utils/filter'; import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; const ICON_TYPE = 'minusInCircle'; -const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOutButton', { +const ICON_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOutButtonIcon', { + defaultMessage: 'Filter Out', +}); +const CELL_ACTION_TITLE = i18n.translate('xpack.threatIntelligence.queryBar.filterOutCellAction', { defaultMessage: 'Filter Out', }); @@ -33,70 +31,36 @@ export interface FilterOutProps { * Value used to filter in /out in the KQL bar. */ field: string; - /** - * Dictates the way the FilterOut component is rendered depending on the situation in which it's used - */ - type?: ComponentType; - /** - * Display component for when the FilterIn component is used within a DataGrid - */ - as?: typeof EuiButtonEmpty | typeof EuiButtonIcon; /** * Used for unit and e2e tests. */ ['data-test-subj']?: string; } +export interface FilterOutCellActionProps extends FilterOutProps { + /** + * Display component for when the FilterIn component is used within an {@link EuiDataGrid}. + */ + Component: typeof EuiButtonEmpty | typeof EuiButtonIcon; +} + /** * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. * - * The component has 3 renders depending on where it's used: within a EuiContextMenu, a EuiDataGrid or not. + * This component renders an {@link EuiButtonIcon}. * - * @returns filter out button + * @returns filter out button icon */ -export const FilterOut: VFC = ({ data, field, type, as: Component, ...props }) => { - const styles = useStyles(); - - const { filterManager } = useIndicatorsFiltersContext(); - - const { key, value } = - typeof data === 'string' ? { key: field, value: data } : getIndicatorFieldAndValue(data, field); - - const filterOut = useCallback(() => { - const existingFilters: Filter[] = filterManager.getFilters(); - const newFilters: Filter[] = updateFiltersArray(existingFilters, key, value, FilterOutConst); - filterManager.setFilters(newFilters); - }, [filterManager, key, value]); - - if (!fieldAndValueValid(key, value)) { +export const FilterOutButtonIcon: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterOut }); + if (!filterFn) { return <>; } - if (type === ComponentType.EuiDataGrid) { - return ( - -
    - {/* @ts-ignore*/} - -
    -
    - ); - } - - if (type === ComponentType.ContextMenu) { - return ( - - Filter Out - - ); - } - return ( = ({ data, field, type, as: Componen iconSize="s" size="xs" color="primary" - onClick={filterOut} - {...props} + onClick={filterFn} + data-test-subj={dataTestSub} /> ); }; + +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component is to be used in an EuiContextMenu. + * + * @returns filter in item for a context menu + */ +export const FilterOutContextMenu: VFC = ({ + data, + field, + 'data-test-subj': dataTestSub, +}) => { + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterOut }); + if (!filterFn) { + return <>; + } + + return ( + + + + ); +}; + +/** + * Retrieves the indicator's field and value, then creates a new {@link Filter} and adds it to the {@link FilterManager}. + * + * This component is to be used in an EuiDataGrid. + * + * @returns filter in button for data grid + */ +export const FilterOutCellAction: VFC = ({ + data, + field, + Component, + 'data-test-subj': dataTestSub, +}) => { + const styles = useStyles(); + + const { filterFn } = useFilterInOut({ indicator: data, field, filterType: FilterOut }); + if (!filterFn) { + return <>; + } + + return ( + +
    + {/* @ts-ignore*/} + +
    +
    + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts new file mode 100644 index 000000000000..6099ed848667 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, RenderHookResult, Renderer } from '@testing-library/react-hooks'; +import { + generateMockIndicator, + generateMockUrlIndicator, + Indicator, +} from '../../../../common/types/indicator'; +import { TestProvidersComponent } from '../../../common/mocks/test_providers'; +import { useFilterInOut, UseFilterInValue } from './use_filter_in_out'; +import { FilterIn } from '../utils/filter'; + +describe('useFilterInOut()', () => { + let hookResult: RenderHookResult<{}, UseFilterInValue, Renderer>; + + it('should return empty object if Indicator is incorrect', () => { + const indicator: Indicator = generateMockIndicator(); + indicator.fields['threat.indicator.name'] = ['wrong']; + const field: string = 'field'; + const filterType = FilterIn; + + hookResult = renderHook(() => useFilterInOut({ indicator, field, filterType }), { + wrapper: TestProvidersComponent, + }); + expect(hookResult.result.current).toEqual({}); + }); + + it('should return filterFn for Indicator', () => { + const indicator: Indicator = generateMockUrlIndicator(); + const field: string = 'threat.indicator.name'; + const filterType = FilterIn; + + hookResult = renderHook(() => useFilterInOut({ indicator, field, filterType }), { + wrapper: TestProvidersComponent, + }); + + expect(hookResult.result.current).toHaveProperty('filterFn'); + }); + + it('should return filterFn for string', () => { + const indicator: string = '0.0.0.0'; + const field: string = 'threat.indicator.name'; + const filterType = FilterIn; + + hookResult = renderHook(() => useFilterInOut({ indicator, field, filterType }), { + wrapper: TestProvidersComponent, + }); + + expect(hookResult.result.current).toHaveProperty('filterFn'); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts new file mode 100644 index 000000000000..d44bb8528afa --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { Filter } from '@kbn/es-query'; +import { useIndicatorsFiltersContext } from '../../indicators/hooks/use_indicators_filters_context'; +import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../indicators/utils/field_value'; +import { FilterIn, FilterOut, updateFiltersArray } from '../utils/filter'; +import { Indicator } from '../../../../common/types/indicator'; + +export interface UseFilterInParam { + /** + * Indicator used to retrieve the field and value then use to update the filters + */ + indicator: Indicator | string; + /** + * Value used to filter in /out in the KQL bar. + */ + field: string; + /** + * To filter in or out. + */ + filterType: typeof FilterIn | typeof FilterOut; +} + +export interface UseFilterInValue { + /** + * Filter function to run on click event. + */ + filterFn: (() => void) | undefined; +} + +/** + * Custom hook that uses an indicator, a field and a type (FilterIn or FilterOut) and returns the filter function. + * + */ +export const useFilterInOut = ({ + indicator, + field, + filterType, +}: UseFilterInParam): UseFilterInValue => { + const { filterManager } = useIndicatorsFiltersContext(); + + const { key, value } = + typeof indicator === 'string' + ? { key: field, value: indicator } + : getIndicatorFieldAndValue(indicator, field); + + const filterFn = useCallback((): void => { + const existingFilters = filterManager.getFilters(); + const newFilters: Filter[] = updateFiltersArray(existingFilters, key, value, filterType); + filterManager.setFilters(newFilters); + }, [filterManager, filterType, key, value]); + + if (!fieldAndValueValid(key, value)) { + return {} as unknown as UseFilterInValue; + } + + return { + filterFn, + }; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.stories.tsx index ee3f2d67ed91..beefdafd9d59 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.stories.tsx @@ -9,13 +9,12 @@ import React from 'react'; import { Story } from '@storybook/react'; import { CoreStart } from '@kbn/core/public'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { EuiContextMenuPanel } from '@elastic/eui'; import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; -import { EMPTY_VALUE } from '../../../../../common/constants'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; -import { AddToTimeline } from './add_to_timeline'; +import { AddToTimelineButtonIcon, AddToTimelineContextMenu } from './add_to_timeline'; export default { - component: AddToTimeline, title: 'AddToTimeline', }; @@ -25,28 +24,23 @@ const KibanaReactContext = createKibanaReactContext({ timelines: mockKibanaTimelinesService, } as unknown as CoreStart); -export const Default: Story = () => { +export const ButtonIcon: Story = () => { const mockData: Indicator = generateMockIndicator(); return ( - + ); }; -export const WithIndicator: Story = () => { - const mockData: string = 'ip'; +export const ContextMenu: Story = () => { + const mockData: Indicator = generateMockIndicator(); + const items = []; return ( - + ); }; - -export const EmptyValue: Story = () => ( - - - -); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.test.tsx index b8ca854613b0..85e581bc8dc1 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { EMPTY_VALUE } from '../../../../../common/constants'; -import { AddToTimeline } from './add_to_timeline'; +import { AddToTimelineButtonIcon } from './add_to_timeline'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; describe('', () => { @@ -19,7 +19,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); @@ -31,7 +31,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); @@ -43,7 +43,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); @@ -56,7 +56,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); @@ -69,7 +69,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); @@ -81,7 +81,7 @@ describe('', () => { const component = render( - + ); expect(component).toMatchSnapshot(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx index 5b33a3bfeaa3..44f9df4225ba 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/add_to_timeline/add_to_timeline.tsx @@ -13,7 +13,6 @@ import { EuiContextMenuItem, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { generateDataProvider } from '../../utils/data_provider'; -import { ComponentType } from '../../../../../common/types/component_type'; import { fieldAndValueValid, getIndicatorFieldAndValue, @@ -21,6 +20,18 @@ import { import { useKibana } from '../../../../hooks/use_kibana'; import { Indicator } from '../../../../../common/types/indicator'; import { useStyles } from './styles'; +import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; + +const BUTTON_ICON_TOOLTIP = i18n.translate( + 'xpack.threatIntelligence.timeline.addToTimelineButtonIcon', + { defaultMessage: 'Add to Timeline' } +); +const CELL_ACTION_TOOLTIP = i18n.translate( + 'xpack.threatIntelligence.timeline.addToTimelineCellAction', + { + defaultMessage: 'Add to Timeline', + } +); export interface AddToTimelineProps { /** @@ -32,30 +43,61 @@ export interface AddToTimelineProps { */ field: string; /** - * Dictates the way the FilterIn component is rendered depending on the situation in which it's used + * Used for unit and e2e tests. */ - type?: ComponentType; + ['data-test-subj']?: string; +} + +export interface AddToTimelineCellActionProps extends AddToTimelineProps { /** * Only used with `EuiDataGrid` (see {@link AddToTimelineButtonProps}). */ - as?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - /** - * Used for unit and e2e tests. - */ - ['data-test-subj']?: string; + Component: typeof EuiButtonEmpty | typeof EuiButtonIcon; } /** - * Add to timeline button, used in many places throughout the TI plugin. - * Support being passed a {@link Indicator} or a string, can be used in a `EuiDataGrid` or as a normal button. - * Leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) + * Add to timeline feature, leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) * Clicking on the button will add a key-value pair to an Untitled timeline. * - * The component has 2 renders depending on where it's used: within a EuiContextMenu or not. + * This component is renders an {@link EuiButtonIcon}. * - * @returns add to timeline button or an empty component. + * @returns add to timeline button or an empty component */ -export const AddToTimeline: VFC = ({ data, field, type, as, ...props }) => { +export const AddToTimelineButtonIcon: VFC = ({ + data, + field, + 'data-test-subj': dataTestSubj, +}) => { + const addToTimelineButton = + useKibana().services.timelines.getHoverActions().getAddToTimelineButton; + + const { addToTimelineProps } = useAddToTimeline({ indicator: data, field }); + if (!addToTimelineProps) { + return <>; + } + + return ( + + + {addToTimelineButton(addToTimelineProps)} + + + ); +}; + +/** + * Add to timeline feature, leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) + * Clicking on the button will add a key-value pair to an Untitled timeline. + * + * This component is to be used in an EuiContextMenu. + * + * @returns add to timeline item for a context menu + */ +export const AddToTimelineContextMenu: VFC = ({ + data, + field, + 'data-test-subj': dataTestSubj, +}) => { const styles = useStyles(); const contextMenuRef = useRef(null); @@ -81,37 +123,55 @@ export const AddToTimeline: VFC = ({ data, field, type, as, // Use case is for the barchart legend (for example). // We can't use the addToTimelineButton directly because the UI doesn't work in a EuiContextMenu. // We hide it and use the defaultFocusedButtonRef props to programmatically click it. - if (type === ComponentType.ContextMenu) { - addToTimelineProps.defaultFocusedButtonRef = contextMenuRef; - - return ( - <> -
    {addToTimelineButton(addToTimelineProps)}
    - contextMenuRef.current?.click()} - {...props} - > - - - - ); - } + addToTimelineProps.defaultFocusedButtonRef = contextMenuRef; + + return ( + <> +
    {addToTimelineButton(addToTimelineProps)}
    + contextMenuRef.current?.click()} + data-test-subj={dataTestSubj} + > + + + + ); +}; - if (as) addToTimelineProps.Component = as; +/** + * Add to timeline feature, leverages the built-in functionality retrieves from the timeLineService (see ThreatIntelligenceSecuritySolutionContext in x-pack/plugins/threat_intelligence/public/types.ts) + * Clicking on the button will add a key-value pair to an Untitled timeline. + * + * This component is to be used as a cellAction in an {@link EuiDataGrid}. + * + * @returns add to timeline button or an empty component + */ +export const AddToTimelineCellAction: VFC = ({ + data, + field, + Component, + 'data-test-subj': dataTestSubj, +}) => { + const addToTimelineButton = + useKibana().services.timelines.getHoverActions().getAddToTimelineButton; + + const { addToTimelineProps } = useAddToTimeline({ indicator: data, field }); + if (!addToTimelineProps) { + return <>; + } + addToTimelineProps.Component = Component; return ( - - {addToTimelineButton(addToTimelineProps)} + + + {addToTimelineButton(addToTimelineProps)} + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/__snapshots__/investigate_in_timeline.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/__snapshots__/investigate_in_timeline.test.tsx.snap new file mode 100644 index 000000000000..0db32e3424f5 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/__snapshots__/investigate_in_timeline.test.tsx.snap @@ -0,0 +1,313 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render button when Indicator data is correct 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should render empty component when Indicator data is incorrect 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + , + "container":
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should render button icon when Indicator data is correct 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + + + +
    + , + "container":
    + + + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` should render empty component when calculated value is - 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + , + "container":
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/index.ts b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/index.ts similarity index 83% rename from x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/index.ts rename to x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/index.ts index d562e618a664..34bd1d7d5627 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/index.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './investigate_in_timeline_button'; +export * from './investigate_in_timeline'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.stories.tsx similarity index 67% rename from x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.stories.tsx index 15c5bc0c23ed..08fe4b782c2c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.stories.tsx @@ -9,16 +9,23 @@ import React from 'react'; import { Story } from '@storybook/react'; import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { generateMockUrlIndicator } from '../../../../../common/types/indicator'; -import { InvestigateInTimelineButtonIcon } from './investigate_in_timeline_button_icon'; +import { InvestigateInTimelineButton, InvestigateInTimelineButtonIcon } from '.'; export default { - component: InvestigateInTimelineButtonIcon, - title: 'InvestigateInTimelineButtonIcon', + title: 'InvestigateInTimeline', }; const mockIndicator = generateMockUrlIndicator(); -export const Default: Story = () => { +export const Button: Story = () => { + return ( + + + + ); +}; + +export const ButtonIcon: Story = () => { return ( diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.test.tsx new file mode 100644 index 000000000000..cd7aba14fff5 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.test.tsx @@ -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. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { + generateMockIndicator, + generateMockUrlIndicator, + Indicator, +} from '../../../../../common/types/indicator'; +import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; +import { InvestigateInTimelineButton, InvestigateInTimelineButtonIcon } from '.'; +import { EMPTY_VALUE } from '../../../../../common/constants'; + +describe('', () => { + describe('', () => { + it('should render button when Indicator data is correct', () => { + const mockData: Indicator = generateMockUrlIndicator(); + const mockId = 'mockId'; + + const component = render( + + + + ); + + expect(component.getByTestId(mockId)).toBeInTheDocument(); + expect(component).toMatchSnapshot(); + }); + + it('should render empty component when Indicator data is incorrect', () => { + const mockData: Indicator = generateMockIndicator(); + mockData.fields['threat.indicator.first_seen'] = ['']; + + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); + }); + + describe('', () => { + it('should render button icon when Indicator data is correct', () => { + const mockData: Indicator = generateMockUrlIndicator(); + const mockId = 'mockId'; + + const component = render( + + + + ); + + expect(component.getByTestId(mockId)).toBeInTheDocument(); + expect(component).toMatchSnapshot(); + }); + + it(`should render empty component when calculated value is ${EMPTY_VALUE}`, () => { + const mockData: Indicator = generateMockIndicator(); + mockData.fields['threat.indicator.first_seen'] = ['']; + + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.tsx new file mode 100644 index 000000000000..cd1f7ce2a2d9 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline/investigate_in_timeline.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { VFC } from 'react'; +import { EuiButton, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { useInvestigateInTimeline } from '../../hooks/use_investigate_in_timeline'; +import { Indicator } from '../../../../../common/types/indicator'; + +const BUTTON_ICON_LABEL: string = i18n.translate( + 'xpack.threatIntelligence.timeline.investigateInTimelineButtonIcon', + { + defaultMessage: 'Investigate in Timeline', + } +); + +export interface InvestigateInTimelineButtonProps { + /** + * Value passed to the timeline. Used in combination with field if is type of {@link Indicator}. + */ + data: Indicator; + /** + * Used for unit and e2e tests. + */ + ['data-test-subj']?: string; +} + +/** + * Investigate in timeline button, uses the InvestigateInTimelineAction component (x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx) + * retrieved from the SecuritySolutionContext. + * + * This component renders an {@link EuiButton}. + * + * @returns add to timeline button + */ +export const InvestigateInTimelineButton: VFC = ({ + data, + 'data-test-subj': dataTestSub, +}) => { + const { investigateInTimelineFn } = useInvestigateInTimeline({ indicator: data }); + if (!investigateInTimelineFn) { + return <>; + } + + return ( + + + + ); +}; + +/** + * Investigate in timeline button uses the InvestigateInTimelineAction component (x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx) + * retrieved from the SecuritySolutionContext. + * + * This component renders an {@link EuiButtonIcon}. + * + * @returns add to timeline button icon + */ +export const InvestigateInTimelineButtonIcon: VFC = ({ + data, + 'data-test-subj': dataTestSub, +}) => { + const { investigateInTimelineFn } = useInvestigateInTimeline({ indicator: data }); + if (!investigateInTimelineFn) { + return <>; + } + + return ( + + + + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/__snapshots__/investigate_in_timeline_button.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/__snapshots__/investigate_in_timeline_button.test.tsx.snap deleted file mode 100644 index a01cfb51e66d..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/__snapshots__/investigate_in_timeline_button.test.tsx.snap +++ /dev/null @@ -1,155 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should render button when Indicator data is correct 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
    - -
    - , - "container":
    - -
    , - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; - -exports[` should render empty component when Indicator data is incorrect 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
    - , - "container":
    , - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.stories.tsx deleted file mode 100644 index c3c8e65001a3..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Story } from '@storybook/react'; -import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; -import { generateMockUrlIndicator } from '../../../../../common/types/indicator'; -import { InvestigateInTimelineButton } from './investigate_in_timeline_button'; - -export default { - component: InvestigateInTimelineButton, - title: 'InvestigateInTimelineButton', -}; - -const mockIndicator = generateMockUrlIndicator(); - -export const Default: Story = () => { - return ( - - - - ); -}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.test.tsx deleted file mode 100644 index b064ead7e645..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import { - generateMockIndicator, - generateMockUrlIndicator, - Indicator, -} from '../../../../../common/types/indicator'; -import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { InvestigateInTimelineButton } from './investigate_in_timeline_button'; - -describe('', () => { - it('should render button when Indicator data is correct', () => { - const mockData: Indicator = generateMockUrlIndicator(); - const mockId = 'mockId'; - - const component = render( - - - - ); - - expect(component.getByTestId(mockId)).toBeInTheDocument(); - expect(component).toMatchSnapshot(); - }); - - it('should render empty component when Indicator data is incorrect', () => { - const mockData: Indicator = generateMockIndicator(); - mockData.fields['threat.indicator.first_seen'] = ['']; - - const component = render( - - - - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.tsx deleted file mode 100644 index 479f17527562..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button/investigate_in_timeline_button.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { VFC } from 'react'; -import { EuiButton } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useInvestigateInTimeline } from '../../hooks/use_investigate_in_timeline'; -import { Indicator } from '../../../../../common/types/indicator'; - -export interface InvestigateInTimelineButtonProps { - /** - * Value passed to the timeline. Used in combination with field if is type of {@link Indicator}. - */ - data: Indicator; - /** - * Used for unit and e2e tests. - */ - ['data-test-subj']?: string; -} - -/** - * Investigate in timeline button, supports being passed a {@link Indicator}. - * This implementation uses the InvestigateInTimelineAction component (x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx) - * retrieved from the SecuritySolutionContext. - * - * @returns add to timeline button or an empty component. - */ -export const InvestigateInTimelineButton: VFC = ({ - data, - ...props -}) => { - const { onClick } = useInvestigateInTimeline({ indicator: data }); - - if (!onClick) { - return <>; - } - - return ( - - - - ); -}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/__snapshots__/investigate_in_timeline_button_icon.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/__snapshots__/investigate_in_timeline_button_icon.test.tsx.snap deleted file mode 100644 index 263e893f3f8b..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/__snapshots__/investigate_in_timeline_button_icon.test.tsx.snap +++ /dev/null @@ -1,159 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should render button icon when Indicator data is correct 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
    - - - -
    - , - "container":
    - - - -
    , - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; - -exports[` should render empty component when calculated value is - 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
    - , - "container":
    , - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/index.ts b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/index.ts deleted file mode 100644 index 6ed30045b29b..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './investigate_in_timeline_button_icon'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.test.tsx deleted file mode 100644 index 3cdaa0528ca5..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import { - generateMockIndicator, - generateMockUrlIndicator, - Indicator, -} from '../../../../../common/types/indicator'; -import { EMPTY_VALUE } from '../../../../../common/constants'; -import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { InvestigateInTimelineButtonIcon } from './investigate_in_timeline_button_icon'; - -describe('', () => { - it('should render button icon when Indicator data is correct', () => { - const mockData: Indicator = generateMockUrlIndicator(); - const mockId = 'mockId'; - - const component = render( - - - - ); - - expect(component.getByTestId(mockId)).toBeInTheDocument(); - expect(component).toMatchSnapshot(); - }); - - it(`should render empty component when calculated value is ${EMPTY_VALUE}`, () => { - const mockData: Indicator = generateMockIndicator(); - mockData.fields['threat.indicator.first_seen'] = ['']; - - const component = render( - - - - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.tsx deleted file mode 100644 index 888f420a3cac..000000000000 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/components/investigate_in_timeline_button_icon/investigate_in_timeline_button_icon.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { VFC } from 'react'; -import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { useInvestigateInTimeline } from '../../hooks/use_investigate_in_timeline'; -import { Indicator } from '../../../../../common/types/indicator'; - -const BUTTON_LABEL: string = i18n.translate( - 'xpack.threatIntelligence.investigateInTimelineButtonIcon', - { - defaultMessage: 'Investigate in Timeline', - } -); - -export interface InvestigateInTimelineButtonIconProps { - /** - * Value passed to the timeline. Used in combination with field if is type of {@link Indicator}. - */ - data: Indicator; - /** - * Used for unit and e2e tests. - */ - ['data-test-subj']?: string; -} - -/** - * Investigate in timeline button, supports being passed a {@link Indicator}. - * This implementation uses the InvestigateInTimelineAction component (x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx) - * retrieved from the SecuritySolutionContext. - * - * @returns add to timeline button or an empty component. - */ -export const InvestigateInTimelineButtonIcon: VFC = ({ - data, - ...props -}) => { - const { onClick } = useInvestigateInTimeline({ indicator: data }); - - if (!onClick) { - return <>; - } - - return ( - - - - ); -}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/index.ts b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/index.ts index b4e2c354c6df..fa8129790129 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/index.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/index.ts @@ -5,4 +5,5 @@ * 2.0. */ +export * from './use_add_to_timeline'; export * from './use_investigate_in_timeline'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.test.tsx new file mode 100644 index 000000000000..a92c75227310 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EMPTY_VALUE } from '../../../../common/constants'; +import { renderHook, RenderHookResult, Renderer } from '@testing-library/react-hooks'; +import { + generateMockIndicator, + generateMockUrlIndicator, + Indicator, +} from '../../../../common/types/indicator'; +import { TestProvidersComponent } from '../../../common/mocks/test_providers'; +import { useAddToTimeline, UseAddToTimelineValue } from './use_add_to_timeline'; + +describe('useInvestigateInTimeline()', () => { + let hookResult: RenderHookResult<{}, UseAddToTimelineValue, Renderer>; + + xit('should return empty object if Indicator is incorrect', () => { + const indicator: Indicator = generateMockIndicator(); + indicator.fields['threat.indicator.name'] = ['wrong']; + const field = 'threat.indicator.name'; + + hookResult = renderHook(() => useAddToTimeline({ indicator, field }), { + wrapper: TestProvidersComponent, + }); + expect(hookResult.result.current).toEqual({}); + }); + + it(`should return empty object if indicator string is ${EMPTY_VALUE}`, () => { + const indicator: string = EMPTY_VALUE; + const field = 'threat.indicator.ip'; + + hookResult = renderHook(() => useAddToTimeline({ indicator, field }), { + wrapper: TestProvidersComponent, + }); + expect(hookResult.result.current).toEqual({}); + }); + + xit('should return empty object if field is incorrect', () => { + const indicator: Indicator = generateMockIndicator(); + const field = 'abc'; + + hookResult = renderHook(() => useAddToTimeline({ indicator, field }), { + wrapper: TestProvidersComponent, + }); + expect(hookResult.result.current).toEqual({}); + }); + + xit('should return addToTimelineProps', () => { + const indicator: Indicator = generateMockUrlIndicator(); + const field = 'threat.indicator.ip'; + + hookResult = renderHook(() => useAddToTimeline({ indicator, field }), { + wrapper: TestProvidersComponent, + }); + + expect(hookResult.result.current).toHaveProperty('addToTimelineProps'); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.ts b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.ts new file mode 100644 index 000000000000..ab69481d3b52 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_add_to_timeline.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 { DataProvider } from '@kbn/timelines-plugin/common'; +import { AddToTimelineButtonProps } from '@kbn/timelines-plugin/public'; +import { generateDataProvider } from '../utils/data_provider'; +import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../indicators/utils/field_value'; +import { Indicator } from '../../../../common/types/indicator'; + +export interface UseAddToTimelineParam { + /** + * Indicator used to retrieve the field and value then passed to the Investigate in Timeline logic + */ + indicator: Indicator | string; + /** + * Indicator's field to retrieve indicator's value + */ + field: string; +} + +export interface UseAddToTimelineValue { + /** + * Props to pass to the addToTimeline feature. + */ + addToTimelineProps: AddToTimelineButtonProps | undefined; +} + +/** + * Custom hook that gets an {@link Indicator}, retrieves the field (from the RawIndicatorFieldId.Name) + * and value, then creates DataProviders used to do the Investigate in Timeline logic + * (see /kibana/x-pack/plugins/security_solution/public/threat_intelligence/use_investigate_in_timeline.ts) + */ +export const useAddToTimeline = ({ + indicator, + field, +}: UseAddToTimelineParam): UseAddToTimelineValue => { + const { key, value } = + typeof indicator === 'string' + ? { key: field, value: indicator } + : getIndicatorFieldAndValue(indicator, field); + + if (!fieldAndValueValid(key, value)) { + return {} as unknown as UseAddToTimelineValue; + } + + const dataProvider: DataProvider[] = [generateDataProvider(key, value as string)]; + + const addToTimelineProps: AddToTimelineButtonProps = { + dataProvider, + field: key, + ownFocus: false, + }; + + return { + addToTimelineProps, + }; +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.test.tsx index cbc040a936dc..30c42d7096f2 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.test.tsx @@ -13,6 +13,7 @@ import { import { generateMockIndicator, generateMockUrlIndicator, + Indicator, } from '../../../../common/types/indicator'; import { TestProvidersComponent } from '../../../common/mocks/test_providers'; @@ -20,7 +21,7 @@ describe('useInvestigateInTimeline()', () => { let hookResult: RenderHookResult<{}, UseInvestigateInTimelineValue, Renderer>; it('should return empty object if Indicator is incorrect', () => { - const indicator = generateMockIndicator(); + const indicator: Indicator = generateMockIndicator(); indicator.fields['threat.indicator.name'] = ['wrong']; hookResult = renderHook(() => useInvestigateInTimeline({ indicator }), { @@ -29,13 +30,13 @@ describe('useInvestigateInTimeline()', () => { expect(hookResult.result.current).toEqual({}); }); - it('should return ', () => { - const indicator = generateMockUrlIndicator(); + it('should return investigateInTimelineFn', () => { + const indicator: Indicator = generateMockUrlIndicator(); hookResult = renderHook(() => useInvestigateInTimeline({ indicator }), { wrapper: TestProvidersComponent, }); - expect(hookResult.result.current).toHaveProperty('onClick'); + expect(hookResult.result.current).toHaveProperty('investigateInTimelineFn'); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.ts b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.ts index 4f79990d00c3..efae8b441a67 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/timeline/hooks/use_investigate_in_timeline.ts @@ -26,7 +26,10 @@ export interface UseInvestigateInTimelineParam { } export interface UseInvestigateInTimelineValue { - onClick: (() => Promise) | undefined; + /** + * Investigate in Timeline function to run on click event. + */ + investigateInTimelineFn: (() => Promise) | undefined; } /** @@ -51,13 +54,13 @@ export const useInvestigateInTimeline = ({ const to = unwrapValue(indicator, RawIndicatorFieldId.TimeStamp) as string; const from = moment(to).subtract(10, 'm').toISOString(); - const investigateInTimelineClick = securitySolutionContext?.getUseInvestigateInTimeline({ + const investigateInTimelineFn = securitySolutionContext?.getUseInvestigateInTimeline({ dataProviders, from, to, }); return { - onClick: investigateInTimelineClick, + investigateInTimelineFn, }; }; From 3ca18d9fe6df54fd2f82b08d60776690560241aa Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Thu, 29 Sep 2022 17:45:49 +0200 Subject: [PATCH 131/185] [Security Solution][Endpoint][Response Actions] Add comment to expand section on actions log table (#141938) fixes elastic/security-team/issues/5058 --- .../response_actions_log.test.tsx | 1 + .../endpoint_response_actions_list/translations.tsx | 6 ++++++ .../use_response_actions_log_table.tsx | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 556c76529633..b7f529e03edf 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -408,6 +408,7 @@ describe('Response actions history', () => { 'Execution completed', 'Input', 'Parameters', + 'Comment', 'Output:', ] ); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index a90f12bc2d24..36dfc76dc1d9 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -65,6 +65,12 @@ export const OUTPUT_MESSAGES = Object.freeze({ defaultMessage: 'Execution completed', } ), + comment: i18n.translate( + 'xpack.securitySolution.responseActionsList.list.item.expandSection.comment', + { + defaultMessage: 'Comment', + } + ), }, }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx index e4dd30b46812..443eac84c6b1 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/use_response_actions_log_table.tsx @@ -126,6 +126,7 @@ export const useResponseActionsLogTable = ({ wasSuccessful, isExpired, command: _command, + comment, parameters, } = item; @@ -157,6 +158,10 @@ export const useResponseActionsLogTable = ({ title: OUTPUT_MESSAGES.expandSection.parameters, description: parametersList ? parametersList : emptyValue, }, + { + title: OUTPUT_MESSAGES.expandSection.comment, + description: comment ? comment : emptyValue, + }, ].map(({ title, description }) => { return { title: {title}, From b41a07f85a94cdc76f1bd1db0cd3d31bb892a5c0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 29 Sep 2022 11:48:49 -0500 Subject: [PATCH 132/185] [eslint/imports/no-boundary-crossing] don't allow package tests to import outside of packages (#142166) --- .../src/rules/no_boundary_crossing.ts | 37 ++++++++++++---- .../src/pkg_info.ts | 2 + .../src/repo_path.ts | 1 + .../header/header.test.tsx | 2 +- .../src/exception_item_card/index.test.tsx | 4 +- .../exception_item_card/meta/meta.test.tsx | 2 +- .../exception_items/exception_items.test.tsx | 2 +- .../src/test_helpers/comments.mock.ts | 18 ++++++++ .../exception_list_item_schema.mock.ts | 42 +++++++++++++++++++ 9 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 packages/kbn-securitysolution-exception-list-components/src/test_helpers/comments.mock.ts create mode 100644 packages/kbn-securitysolution-exception-list-components/src/test_helpers/exception_list_item_schema.mock.ts 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 4f3defc21d29..832b4d2e3c67 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 @@ -19,15 +19,17 @@ import { getSourcePath } from '../helpers/source'; import { getRepoSourceClassifier } from '../helpers/repo_source_classifier'; import { getImportResolver } from '../get_import_resolver'; -const IMPORTABLE_FROM: Record = { +const ANY_FILE_IN_BAZEL = Symbol(); + +const IMPORTABLE_FROM: Record = { 'non-package': ['non-package', 'server package', 'browser package', 'common package', 'static'], 'server package': ['common package', 'server package', 'static'], 'browser package': ['common package', 'browser package', 'static'], 'common package': ['common package', 'static'], static: [], - 'tests or mocks': '*', - tooling: '*', + 'tests or mocks': ANY_FILE_IN_BAZEL, + tooling: ANY_FILE_IN_BAZEL, }; const toList = (strings: string[]) => { @@ -87,6 +89,7 @@ export const NoBoundaryCrossingRule: Rule.RuleModule = { }, messages: { TYPE_MISMATCH: `"{{importedType}}" code can not be imported from "{{ownType}}" code.{{suggestion}}`, + FILE_OUTSIDE_OF_PACKAGE: `"{{ownType}}" code can import any code already within packages, but not files outside of packages.`, }, }, create(context) { @@ -98,12 +101,7 @@ export const NoBoundaryCrossingRule: Rule.RuleModule = { const self = classifier.classify(sourcePath); const importable = IMPORTABLE_FROM[self.type]; - if (importable === '*') { - // don't check imports in files which can import anything - return {}; - } - - return visitAllImportStatements((req, { node, importer }) => { + return visitAllImportStatements((req, { node, importer, type }) => { 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 @@ -121,6 +119,27 @@ export const NoBoundaryCrossingRule: Rule.RuleModule = { const imported = classifier.classify(result.absolute); + if (importable === ANY_FILE_IN_BAZEL) { + if (type === 'jest' && imported.repoRel === 'package.json') { + // we allow jest.mock() calls to mock out the `package.json` file... it's a very + // specific exception for a very specific implementation + return; + } + + if (self.pkgInfo?.isBazelPackage ? imported.pkgInfo?.isBazelPackage : true) { + return; + } + + context.report({ + node: node as ESTree.Node, + messageId: 'FILE_OUTSIDE_OF_PACKAGE', + data: { + ownType: self.type, + }, + }); + return; + } + if (!importable.includes(imported.type)) { context.report({ node: node as ESTree.Node, diff --git a/packages/kbn-repo-source-classifier/src/pkg_info.ts b/packages/kbn-repo-source-classifier/src/pkg_info.ts index 89d66092737b..a7f7100ba959 100644 --- a/packages/kbn-repo-source-classifier/src/pkg_info.ts +++ b/packages/kbn-repo-source-classifier/src/pkg_info.ts @@ -13,4 +13,6 @@ export interface PkgInfo { rel: string; /** Absolute path to the package directory */ pkgDir: string; + /** Is the package a bazel package? If false, then the package is a "synthetic" plugin package */ + isBazelPackage: boolean; } diff --git a/packages/kbn-repo-source-classifier/src/repo_path.ts b/packages/kbn-repo-source-classifier/src/repo_path.ts index 05eef7105a71..cd13adf0cb82 100644 --- a/packages/kbn-repo-source-classifier/src/repo_path.ts +++ b/packages/kbn-repo-source-classifier/src/repo_path.ts @@ -93,6 +93,7 @@ export class RepoPath { pkgDir, pkgId, rel, + isBazelPackage: this.resolver.isBazelPackage(pkgId), }; } } diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx index 78feab598c14..7f7f20dfc234 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx @@ -7,8 +7,8 @@ */ import React from 'react'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListItemSchemaMock } from '../../test_helpers/exception_list_item_schema.mock'; import * as i18n from '../translations'; import { ExceptionItemCardHeader } from './header'; import { fireEvent, render } from '@testing-library/react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx index e97b03607bb6..87a8a3bd3b52 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import { ExceptionItemCard } from '.'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getCommentsArrayMock } from '@kbn/lists-plugin/common/schemas/types/comment.mock'; +import { getExceptionListItemSchemaMock } from '../test_helpers/exception_list_item_schema.mock'; +import { getCommentsArrayMock } from '../test_helpers/comments.mock'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; const ruleReferences: unknown[] = [ diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx index 14bdef771d6b..c5ad9bd7af31 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListItemSchemaMock } from '../../test_helpers/exception_list_item_schema.mock'; import { ExceptionItemCardMetaInfo } from './meta'; import { RuleReference } from '../../types'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx index 3fe2d7eb6d0b..39c429dd1f1d 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListItemSchemaMock } from '../test_helpers/exception_list_item_schema.mock'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ExceptionItems } from './exception_items'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/test_helpers/comments.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/test_helpers/comments.mock.ts new file mode 100644 index 000000000000..3e83aa53f0f2 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/test_helpers/comments.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Comment, CommentsArray } from '@kbn/securitysolution-io-ts-list-types'; + +export const getCommentsMock = (): Comment => ({ + comment: 'some old comment', + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + id: 'uuid_here', +}); + +export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; diff --git a/packages/kbn-securitysolution-exception-list-components/src/test_helpers/exception_list_item_schema.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/test_helpers/exception_list_item_schema.mock.ts new file mode 100644 index 000000000000..40f6d5292f31 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/test_helpers/exception_list_item_schema.mock.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export const getExceptionListItemSchemaMock = ( + overrides?: Partial +): ExceptionListItemSchema => ({ + _version: undefined, + comments: [], + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + description: 'some description', + entries: [ + { + entries: [ + { field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }, + ], + field: 'some.parentField', + type: 'nested', + }, + { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, + ], + id: '1', + item_id: 'endpoint_list_item', + list_id: 'endpoint_list_id', + meta: {}, + name: 'some name', + namespace_type: 'single', + os_types: [], + tags: ['user added string for a tag', 'malware'], + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'simple', + updated_at: '2020-04-20T15:25:31.830Z', + updated_by: 'some user', + ...(overrides || {}), +}); From 5d31e88c5dbe5bfa9337938d91accef7b9d10106 Mon Sep 17 00:00:00 2001 From: "Quynh Nguyen (Quinn)" <43350163+qn895@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:28:24 -0500 Subject: [PATCH 133/185] [ML] Fix links to Discover and Maps and custom URLs for jobs with a query in the datafeed (#141871) Co-authored-by: Dima Arnautov --- .../get_filters_for_datafeed_query.test.ts | 94 +++++++++++++++++++ .../get_filters_for_datafeed_query.ts | 45 +++++++++ .../components/anomalies_table/links_menu.tsx | 27 +++++- .../components/custom_url_editor/utils.js | 17 +++- .../functional/services/ml/custom_urls.ts | 4 +- 5 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.test.ts create mode 100644 x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.ts diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.test.ts b/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.test.ts new file mode 100644 index 000000000000..20b52a773d03 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { getFiltersForDSLQuery } from './get_filters_for_datafeed_query'; + +describe('getFiltersForDSLQuery', () => { + describe('when DSL query contains match_all', () => { + test('returns empty array when query contains a must clause that contains match_all', () => { + const actual = getFiltersForDSLQuery( + { bool: { must: [{ match_all: {} }] } }, + 'dataview-id', + 'test-alias' + ); + expect(actual).toEqual([]); + }); + + test('returns empty array when query contains match_all', () => { + const actual = getFiltersForDSLQuery({ match_all: {} }, 'dataview-id', 'test-alias'); + expect(actual).toEqual([]); + }); + }); + + describe('when DSL query is valid', () => { + const query = { + bool: { + must: [], + filter: [ + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2007-09-29T15:05:14.509Z', + lte: '2022-09-29T15:05:14.509Z', + }, + }, + }, + { + match_phrase: { + response_code: '200', + }, + }, + ], + should: [], + must_not: [], + }, + }; + + test('returns filters with alias', () => { + const actual = getFiltersForDSLQuery(query, 'dataview-id', 'test-alias'); + expect(actual).toEqual([ + { + $state: { store: 'appState' }, + meta: { + alias: 'test-alias', + disabled: false, + index: 'dataview-id', + negate: false, + type: 'custom', + value: + '{"bool":{"must":[],"filter":[{"range":{"@timestamp":{"format":"strict_date_optional_time","gte":"2007-09-29T15:05:14.509Z","lte":"2022-09-29T15:05:14.509Z"}}},{"match_phrase":{"response_code":"200"}}],"should":[],"must_not":[]}}', + }, + query, + }, + ]); + }); + + test('returns empty array when dataViewId is invalid', () => { + const actual = getFiltersForDSLQuery(query, null, 'test-alias'); + expect(actual).toEqual([]); + }); + + test('returns filter with no alias if alias is not provided', () => { + const actual = getFiltersForDSLQuery(query, 'dataview-id'); + expect(actual).toEqual([ + { + $state: { store: 'appState' }, + meta: { + disabled: false, + index: 'dataview-id', + negate: false, + type: 'custom', + value: + '{"bool":{"must":[],"filter":[{"range":{"@timestamp":{"format":"strict_date_optional_time","gte":"2007-09-29T15:05:14.509Z","lte":"2022-09-29T15:05:14.509Z"}}},{"match_phrase":{"response_code":"200"}}],"should":[],"must_not":[]}}', + }, + query, + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.ts b/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.ts new file mode 100644 index 000000000000..08ff962449d2 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/get_filters_for_datafeed_query.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { FilterStateStore } from '@kbn/es-query'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { isEqual } from 'lodash'; + +const defaultEmptyQuery = { bool: { must: [{ match_all: {} }] } }; + +export const getFiltersForDSLQuery = ( + datafeedQuery: QueryDslQueryContainer, + dataViewId: string | null, + alias?: string +) => { + if ( + datafeedQuery && + !isPopulatedObject(datafeedQuery, ['match_all']) && + !isEqual(datafeedQuery, defaultEmptyQuery) && + dataViewId !== null + ) { + return [ + { + meta: { + index: dataViewId, + ...(!!alias ? { alias } : {}), + negate: false, + disabled: false, + type: 'custom', + value: JSON.stringify(datafeedQuery), + }, + query: datafeedQuery as SerializableRecord, + $state: { + store: FilterStateStore.APP_STATE, + }, + }, + ]; + } + return []; +}; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx index aa6c80ee5b92..6ad828bc3966 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx @@ -53,6 +53,7 @@ import { useMlKibana } from '../../contexts/kibana'; import { getFieldTypeFromMapping } from '../../services/mapping_service'; import type { AnomaliesTableRecord } from '../../../../common/types/anomalies'; import { getQueryStringForInfluencers } from './get_query_string_for_influencers'; +import { getFiltersForDSLQuery } from './get_filters_for_datafeed_query'; interface LinksMenuProps { anomaly: AnomaliesTableRecord; bounds: TimeRangeBounds; @@ -78,7 +79,14 @@ export const LinksMenuUI = (props: LinksMenuProps) => { services: { data, share, application }, } = kibana; + const job = useMemo(() => { + return mlJobService.getJob(props.anomaly.jobId); + }, [props.anomaly.jobId]); + const getAnomaliesMapsLink = async (anomaly: AnomaliesTableRecord) => { + const index = job.datafeed_config.indices[0]; + const dataViewId = await getDataViewIdFromName(index); + const initialLayers = getInitialAnomaliesLayers(anomaly.jobId); const anomalyBucketStartMoment = moment(anomaly.source.timestamp).tz(getDateFormatTz()); const anomalyBucketStart = anomalyBucketStartMoment.toISOString(); @@ -104,6 +112,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { }, } : {}), + filters: getFiltersForDSLQuery(job.datafeed_config.query, dataViewId, job.job_id), }); return location; }; @@ -112,6 +121,9 @@ export const LinksMenuUI = (props: LinksMenuProps) => { anomaly: AnomaliesTableRecord, sourceIndicesWithGeoFields: SourceIndicesWithGeoFields ) => { + const index = job.datafeed_config.indices[0]; + const dataViewId = await getDataViewIdFromName(index); + // Create a layer for each of the geoFields const initialLayers = getInitialSourceIndexFieldLayers( sourceIndicesWithGeoFields[anomaly.jobId] @@ -138,10 +150,18 @@ export const LinksMenuUI = (props: LinksMenuProps) => { ); const locator = share.url.locators.get(MAPS_APP_LOCATOR); + const filtersFromDatafeedQuery = getFiltersForDSLQuery( + job.datafeed_config.query, + dataViewId, + job.job_id + ); const location = await locator?.getLocation({ initialLayers, timeRange, - filters: data.query.filterManager.getFilters(), + filters: + filtersFromDatafeedQuery.length > 0 + ? filtersFromDatafeedQuery + : data.query.filterManager.getFilters(), ...(anomaly.entityName && anomaly.entityValue ? { query: { @@ -175,7 +195,6 @@ export const LinksMenuUI = (props: LinksMenuProps) => { } const getDataViewId = async () => { - const job = mlJobService.getJob(props.anomaly.jobId); const index = job.datafeed_config.indices[0]; const dataViewId = await getDataViewIdFromName(index); @@ -246,6 +265,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { language: 'kuery', query: kqlQuery, }, + filters: getFiltersForDSLQuery(job.datafeed_config.query, dataViewId, job.job_id), sort: [['timestamp, asc']], }); @@ -440,7 +460,6 @@ export const LinksMenuUI = (props: LinksMenuProps) => { const categoryId = props.anomaly.entityValue; const record = props.anomaly.source; - const job = mlJobService.getJob(props.anomaly.jobId); if (job === undefined) { // eslint-disable-next-line no-console console.log(`viewExamples(): no job found with ID: ${props.anomaly.jobId}`); @@ -545,7 +564,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { const appStateProps: RisonValue = { index: dataViewId, - filters: [], + filters: getFiltersForDSLQuery(job.datafeed_config.query, dataViewId, job.job_id), }; if (query !== null) { appStateProps.query = query; diff --git a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js index 1824d8e91e74..9c2fa8383b7a 100644 --- a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js @@ -20,6 +20,7 @@ import { getSavedObjectsClient, getDashboard } from '../../../util/dependency_ca import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; import { cleanEmptyKeys } from '@kbn/dashboard-plugin/public'; import { isFilterPinned } from '@kbn/es-query'; +import { getFiltersForDSLQuery } from '../../../components/anomalies_table/get_filters_for_datafeed_query'; export function getNewCustomUrlDefaults(job, dashboards, dataViews) { // Returns the settings object in the format used by the custom URL editor @@ -50,6 +51,11 @@ export function getNewCustomUrlDefaults(job, dashboards, dataViews) { const indicesName = datafeedConfig.indices.join(); const defaultDataViewId = dataViews.find((dv) => dv.title === indicesName)?.id; kibanaSettings.discoverIndexPatternId = defaultDataViewId; + kibanaSettings.filters = getFiltersForDSLQuery( + job.datafeed_config.query, + defaultDataViewId, + job.job_id + ); } return { @@ -133,16 +139,18 @@ async function buildDashboardUrlFromSettings(settings) { const response = await savedObjectsClient.get('dashboard', dashboardId); - // Use the filters from the saved dashboard if there are any. - let filters = []; + // Query from the datafeed config will be saved as custom filters + // Use them if there are set. + let filters = settings.kibanaSettings.filters; // Use the query from the dashboard only if no job entities are selected. let query = undefined; + // Override with filters and queries from saved dashboard if they are available. const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON !== undefined) { const searchSourceData = JSON.parse(searchSourceJSON); - if (searchSourceData.filter !== undefined) { + if (Array.isArray(searchSourceData.filter) && searchSourceData.filter.length > 0) { filters = searchSourceData.filter; } query = searchSourceData.query; @@ -196,7 +204,7 @@ async function buildDashboardUrlFromSettings(settings) { } function buildDiscoverUrlFromSettings(settings) { - const { discoverIndexPatternId, queryFieldNames } = settings.kibanaSettings; + const { discoverIndexPatternId, queryFieldNames, filters } = settings.kibanaSettings; // Add time settings to the global state URL parameter with $earliest$ and // $latest$ tokens which get substituted for times around the time of the @@ -212,6 +220,7 @@ function buildDiscoverUrlFromSettings(settings) { // Add the index pattern and query to the appState part of the URL. const appState = { index: discoverIndexPatternId, + filters, }; // If partitioning field entities have been configured add tokens diff --git a/x-pack/test/functional/services/ml/custom_urls.ts b/x-pack/test/functional/services/ml/custom_urls.ts index 1695b575e1f0..46e145cda1c3 100644 --- a/x-pack/test/functional/services/ml/custom_urls.ts +++ b/x-pack/test/functional/services/ml/custom_urls.ts @@ -111,7 +111,7 @@ export function MachineLearningCustomUrlsProvider({ ); expect(actualLabel).to.eql( expectedLabel, - `Expected custom url item to be '${expectedLabel}' (got '${actualLabel}')` + `Expected custom url label to be '${expectedLabel}' (got '${actualLabel}')` ); }, @@ -123,7 +123,7 @@ export function MachineLearningCustomUrlsProvider({ ); expect(actualUrl).to.eql( expectedUrl, - `Expected custom url item to be '${expectedUrl}' (got '${actualUrl}')` + `Expected custom url value to be '${expectedUrl}' (got '${actualUrl}')` ); }, From 4cdd74dfcd71edaeae536220400310c271ee8a41 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Thu, 29 Sep 2022 19:41:06 +0200 Subject: [PATCH 134/185] Fix SO export sorting algorithm (#142078) * Fix SO export sorting algorithm * improve var name * adapt another unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/export/saved_objects_exporter.test.ts | 6 ++- .../src/export/sort_objects.test.ts | 37 +++++++++++++++++++ .../src/export/sort_objects.ts | 21 ++++++----- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.test.ts index 614c9e3680ac..fed06cbf2f74 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.test.ts @@ -146,16 +146,18 @@ describe('getSortedObjectsForExport()', () => { attributes = {}, sort = [], type = 'index-pattern', + idPrefix = '', }: { attributes?: Record; sort?: string[]; type?: string; + idPrefix?: string; } = {} ) { const hits = []; for (let i = 1; i <= hitCount; i++) { hits.push({ - id: `${i}`, + id: `${idPrefix}${i}`, type, attributes, sort, @@ -247,7 +249,7 @@ describe('getSortedObjectsForExport()', () => { describe('>1k hits', () => { const firstMockHits = generateHits(1000, { sort: ['a', 'b'] }); - const secondMockHits = generateHits(500); + const secondMockHits = generateHits(500, { idPrefix: 'second-hit-' }); test('requests multiple pages', async () => { savedObjectsClient.find.mockResolvedValueOnce({ diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.test.ts index 1f663ea5dbc5..27fbb09a3701 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.test.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ +import { range } from 'lodash'; import { sortObjects } from './sort_objects'; +import type { SavedObject } from '@kbn/core-saved-objects-common'; describe('sortObjects()', () => { test('should return on empty array', () => { @@ -309,6 +311,7 @@ describe('sortObjects()', () => { ] `); }); + test('should not fail on complex circular dependencies', () => { const docs = [ { @@ -424,4 +427,38 @@ describe('sortObjects()', () => { ] `); }); + + test('should not fail on large graph of objects', () => { + // create an object that references all objects with a higher `index` up to `depth`. + const createComplexNode = (index: number, depth: number): SavedObject => { + return { + type: 'test', + id: `${index}`, + attributes: {}, + references: range(index + 1, depth).map((refIndex) => ({ + type: 'test', + id: `${refIndex}`, + name: `test-${refIndex}`, + })), + }; + }; + + const createComplexGraph = (depth: number): SavedObject[] => { + const nodes: SavedObject[] = []; + for (let i = 0; i < depth; i++) { + nodes.push(createComplexNode(i, depth)); + } + return nodes; + }; + + const depth = 100; + const graph = createComplexGraph(depth); + const sorted = sortObjects(graph); + + expect(sorted.map(({ type, id }) => `${type}:${id}`)).toEqual( + range(depth) + .reverse() + .map((index) => `test:${index}`) + ); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.ts index 487622877e25..551ba3989e52 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/sort_objects.ts @@ -8,27 +8,29 @@ import type { SavedObject } from '@kbn/core-saved-objects-common'; +const getId = (object: { type: string; id: string }) => `${object.type}:${object.id}`; + export function sortObjects(savedObjects: SavedObject[]): SavedObject[] { - const path = new Set(); + const traversed = new Set(); const sorted = new Set(); const objectsByTypeId = new Map( - savedObjects.map((object) => [`${object.type}:${object.id}`, object] as [string, SavedObject]) + savedObjects.map((object) => [getId(object), object] as [string, SavedObject]) ); function includeObjects(objects: SavedObject[]) { for (const object of objects) { - if (path.has(object)) { + const objectId = getId(object); + if (traversed.has(objectId)) { continue; } - const refdObjects = object.references - .map((ref) => objectsByTypeId.get(`${ref.type}:${ref.id}`)) + const objectRefs = object.references + .map((ref) => objectsByTypeId.get(getId(ref))) .filter((ref): ref is SavedObject => !!ref); - if (refdObjects.length) { - path.add(object); - includeObjects(refdObjects); - path.delete(object); + traversed.add(objectId); + if (objectRefs.length) { + includeObjects(objectRefs); } sorted.add(object); @@ -36,5 +38,6 @@ export function sortObjects(savedObjects: SavedObject[]): SavedObject[] { } includeObjects(savedObjects); + return [...sorted]; } From d8948bf9bccbec47d9b2fe50504946ae879aa344 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:50:42 -0600 Subject: [PATCH 135/185] skip failing test suite (#141002) --- .../test_suites/task_manager/task_management.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 123259cadf0c..fd7e07dacb47 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 @@ -54,7 +54,8 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const testHistoryIndex = '.kibana_task_manager_test_result'; - describe('scheduling and running tasks', () => { + // Failing: See https://github.com/elastic/kibana/issues/141002 + describe.skip('scheduling and running tasks', () => { beforeEach(async () => { // clean up before each test return await supertest.delete('/api/sample_tasks').set('kbn-xsrf', 'xxx').expect(200); From 1530d1720a84f43d948925ddba0830892048e063 Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:05:10 +0200 Subject: [PATCH 136/185] [Security Solution][Endpoint][Response Actions] Show correct number of items in response actions history (#142221) * Show correct number of items in page when status filters are selected When there are more items than the page size, the API was returning one less item when a status filter was selected. This commit fixes that. * Tests * some unrelated test cleanup --- .../response_actions_log.test.tsx | 30 +++++----- .../services/actions/action_list.test.ts | 58 ++++++++++++++++++- .../endpoint/services/actions/action_list.ts | 6 +- .../server/endpoint/services/actions/mocks.ts | 21 ++++--- 4 files changed, 86 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index b7f529e03edf..0133e09ac720 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -344,26 +344,20 @@ describe('Response actions history', () => { ); // should have 4 pages each of size 10. - expect(renderResult.getByTestId('pagination-button-0')).toHaveAttribute( - 'aria-label', - 'Page 1 of 4' - ); + expect(getByTestId('pagination-button-0')).toHaveAttribute('aria-label', 'Page 1 of 4'); // toggle page size popover - userEvent.click(renderResult.getByTestId('tablePaginationPopoverButton')); + userEvent.click(getByTestId('tablePaginationPopoverButton')); await waitForEuiPopoverOpen(); // click size 20 - userEvent.click(renderResult.getByTestId('tablePagination-20-rows')); + userEvent.click(getByTestId('tablePagination-20-rows')); - expect(renderResult.getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent( + expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent( 'Showing 1-20 of 33 response actions' ); // should have only 2 pages each of size 20 - expect(renderResult.getByTestId('pagination-button-0')).toHaveAttribute( - 'aria-label', - 'Page 1 of 2' - ); + expect(getByTestId('pagination-button-0')).toHaveAttribute('aria-label', 'Page 1 of 2'); }); it('should show 1-1 record label when only 1 record', async () => { @@ -545,8 +539,10 @@ describe('Response actions history', () => { it('should have a search bar', () => { render(); - userEvent.click(renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); - const searchBar = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`); + + const { getByTestId } = renderResult; + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const searchBar = getByTestId(`${testPrefix}-${filterPrefix}-search`); expect(searchBar).toBeTruthy(); expect(searchBar.querySelector('input')?.getAttribute('placeholder')).toEqual( 'Search actions' @@ -595,10 +591,10 @@ describe('Response actions history', () => { it('should have `clear all` button `disabled` when no selected values', () => { render(); - userEvent.click(renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); - const clearAllButton = renderResult.getByTestId( - `${testPrefix}-${filterPrefix}-clearAllButton` - ); + const { getByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`); expect(clearAllButton.hasAttribute('disabled')).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.test.ts index f08b82a49071..67d527817abf 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.test.ts @@ -14,7 +14,7 @@ import type { LogsEndpointActionResponse, } from '../../../../common/endpoint/types'; import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; -import { getActionList } from './action_list'; +import { getActionList, getActionListByStatus } from './action_list'; import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; import { applyActionListEsSearchMock, @@ -650,3 +650,59 @@ describe('When using `getActionList()', () => { await expect(getActionListPromise).rejects.toBeInstanceOf(CustomHttpRequestError); }); }); + +describe('When using `getActionListByStatus()', () => { + let esClient: ElasticsearchClientMock; + let logger: MockedLogger; + // let endpointActionGenerator: EndpointActionGenerator; + let actionRequests: estypes.SearchResponse; + let actionResponses: estypes.SearchResponse; + let endpointAppContextService: EndpointAppContextService; + + beforeEach(() => { + esClient = elasticsearchServiceMock.createScopedClusterClient().asInternalUser; + logger = loggingSystemMock.createLogger(); + // endpointActionGenerator = new EndpointActionGenerator('seed'); + endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); + endpointAppContextService.start(createMockEndpointAppContextServiceStartContract()); + + actionRequests = createActionRequestsEsSearchResultsMock(undefined); + actionResponses = createActionResponsesEsSearchResultsMock(); + + applyActionListEsSearchMock(esClient, actionRequests, actionResponses); + }); + + afterEach(() => { + endpointAppContextService.stop(); + }); + + it('should return expected output `data` length for selected statuses', async () => { + actionRequests = createActionRequestsEsSearchResultsMock(undefined, true); + actionResponses = createActionResponsesEsSearchResultsMock(); + + applyActionListEsSearchMock(esClient, actionRequests, actionResponses); + // mock metadataService.findHostMetadataForFleetAgents resolved value + (endpointAppContextService.getEndpointMetadataService as jest.Mock) = jest + .fn() + .mockReturnValue({ + findHostMetadataForFleetAgents: jest.fn().mockResolvedValue([]), + }); + + const getActionListByStatusPromise = ({ page }: { page: number }) => + getActionListByStatus({ + esClient, + logger, + metadataService: endpointAppContextService.getEndpointMetadataService(), + page: page ?? 1, + pageSize: 10, + statuses: ['failed', 'pending', 'successful'], + }); + + expect(await (await getActionListByStatusPromise({ page: 1 })).data.length).toEqual(10); + + expect(await (await getActionListByStatusPromise({ page: 2 })).data.length).toEqual(10); + + expect(await (await getActionListByStatusPromise({ page: 3 })).data.length).toEqual(3); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts index eb53a6a4338a..461f707e336b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts @@ -92,8 +92,8 @@ export const getActionListByStatus = async ({ userIds, commands, statuses, - // for size 20 -> page 1: (0, 19), page 2: (20,39) ...etc - data: actionDetailsByStatus.slice((page - 1) * size, size * page - 1), + // for size 20 -> page 1: (0, 20), page 2: (20, 40) ...etc + data: actionDetailsByStatus.slice((page - 1) * size, size * page), total: actionDetailsByStatus.length, }; }; @@ -251,7 +251,7 @@ const getActionDetailsList = async ({ }); // compute action details list for each action id - const actionDetails: ActionDetails[] = normalizedActionRequests.map((action) => { + const actionDetails: ActionListApiResponse['data'] = normalizedActionRequests.map((action) => { // pick only those responses that match the current action id const matchedResponses = categorizedResponses.filter((categorizedResponse) => categorizedResponse.type === 'response' diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts index 80c7a5a6ff6c..23705d6bc43b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts @@ -21,17 +21,22 @@ import { } from '../../../../common/endpoint/constants'; export const createActionRequestsEsSearchResultsMock = ( - agentIds?: string[] + agentIds?: string[], + isMultipleActions: boolean = false ): estypes.SearchResponse => { const endpointActionGenerator = new EndpointActionGenerator('seed'); - return endpointActionGenerator.toEsSearchResponse([ - endpointActionGenerator.generateActionEsHit({ - EndpointActions: { action_id: '123' }, - agent: { id: agentIds ? agentIds : 'agent-a' }, - '@timestamp': '2022-04-27T16:08:47.449Z', - }), - ]); + return isMultipleActions + ? endpointActionGenerator.toEsSearchResponse( + Array.from({ length: 23 }).map(() => endpointActionGenerator.generateActionEsHit()) + ) + : endpointActionGenerator.toEsSearchResponse([ + endpointActionGenerator.generateActionEsHit({ + EndpointActions: { action_id: '123' }, + agent: { id: agentIds ? agentIds : 'agent-a' }, + '@timestamp': '2022-04-27T16:08:47.449Z', + }), + ]); }; export const createActionResponsesEsSearchResultsMock = ( From 1bf14d7ab8681dacad8fb25cc906e15cf96233f6 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Thu, 29 Sep 2022 15:48:36 -0400 Subject: [PATCH 137/185] Fixed pinned filters being backed up into session storage (#142262) --- .../public/application/lib/diff_dashboard_state.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts index 1c57d1bd2afa..e5432b50550e 100644 --- a/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts @@ -101,12 +101,17 @@ export const diffDashboardState = async ({ getEmbeddable ); const optionsAreEqual = getOptionsAreEqual(originalState.options, newState.options); - const filtersAreEqual = getFiltersAreEqual(originalState.filters, newState.filters, true); const controlGroupIsEqual = persistableControlGroupInputIsEqual( originalState.controlGroupInput, newState.controlGroupInput ); + const filterStateDiff = getFiltersAreEqual(originalState.filters, newState.filters, true) + ? {} + : { + filters: newState.filters.filter((f) => !isFilterPinned(f)), + }; + const timeStatediff = getTimeSettingsAreEqual({ currentTimeRestore: newState.timeRestore, lastSaved: { ...pick(originalState, ['timeRange', 'timeRestore', 'refreshInterval']) }, @@ -117,9 +122,9 @@ export const diffDashboardState = async ({ return { ...commonStateDiff, ...(panelsAreEqual ? {} : { panels: newState.panels }), - ...(filtersAreEqual ? {} : { filters: newState.filters }), ...(optionsAreEqual ? {} : { options: newState.options }), ...(controlGroupIsEqual ? {} : { controlGroupInput: newState.controlGroupInput }), + ...filterStateDiff, ...timeStatediff, }; }; @@ -174,7 +179,7 @@ const getFiltersAreEqual = ( ignorePinned?: boolean ): boolean => { return compareFilters( - filtersA, + ignorePinned ? filtersA.filter((f) => !isFilterPinned(f)) : filtersA, ignorePinned ? filtersB.filter((f) => !isFilterPinned(f)) : filtersB, COMPARE_ALL_OPTIONS ); From 907c1059ba65b9aeb1318416fa9a0885cf515031 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:05:22 -0400 Subject: [PATCH 138/185] [Detection Rules] Add 8.5 rules (#142239) Co-authored-by: Mika Ayenson --- .../collection_cloudtrail_logging_created.json | 4 ++-- .../collection_gcp_pub_sub_subscription_creation.json | 5 +++-- .../collection_gcp_pub_sub_topic_creation.json | 5 +++-- ..._drive_ownership_transferred_via_google_workspace.json | 4 ++-- ..._workspace_custom_gmail_route_created_or_modified.json | 4 ++-- .../collection_microsoft_365_new_inbox_rule.json | 4 ++-- .../collection_update_event_hub_auth_rule.json | 4 ++-- .../credential_access_attempted_bypass_of_okta_mfa.json | 4 ++-- ..._access_attempts_to_brute_force_okta_user_account.json | 4 ++-- ...credential_access_aws_iam_assume_role_brute_force.json | 4 ++-- ...access_azure_full_network_packet_capture_detected.json | 4 ++-- .../credential_access_iam_user_addition_to_group.json | 4 ++-- .../credential_access_key_vault_modified.json | 4 ++-- ...ss_microsoft_365_brute_force_user_account_attempt.json | 4 ++-- ..._microsoft_365_potential_password_spraying_attack.json | 4 ++-- ...tial_access_okta_brute_force_or_password_spraying.json | 4 ++-- ...redential_access_root_console_failure_brute_force.json | 4 ++-- .../credential_access_secretsmanager_getsecretvalue.json | 4 ++-- ...credential_access_storage_account_key_regenerated.json | 4 ++-- ...credential_access_user_excessive_sso_logon_errors.json | 4 ++-- .../credential_access_user_impersonation_access.json | 4 ++-- ...cation_removed_from_blocklist_in_google_workspace.json | 4 ++-- ...e_evasion_attempt_to_deactivate_okta_network_zone.json | 4 ++-- ...fense_evasion_attempt_to_delete_okta_network_zone.json | 4 ++-- ...evasion_azure_application_credential_modification.json | 4 ++-- .../defense_evasion_azure_automation_runbook_deleted.json | 4 ++-- .../defense_evasion_azure_blob_permissions_modified.json | 4 ++-- ...efense_evasion_azure_diagnostic_settings_deletion.json | 4 ++-- .../defense_evasion_azure_service_principal_addition.json | 4 ++-- .../defense_evasion_cloudtrail_logging_deleted.json | 4 ++-- .../defense_evasion_cloudtrail_logging_suspended.json | 4 ++-- .../defense_evasion_cloudwatch_alarm_deletion.json | 4 ++-- .../defense_evasion_config_service_rule_deletion.json | 4 ++-- .../defense_evasion_configuration_recorder_stopped.json | 4 ++-- ..._domain_added_to_google_workspace_trusted_domains.json | 4 ++-- .../defense_evasion_ec2_flow_log_deletion.json | 4 ++-- .../defense_evasion_ec2_network_acl_deletion.json | 4 ++-- ...fense_evasion_elasticache_security_group_creation.json | 4 ++-- ...on_elasticache_security_group_modified_or_deleted.json | 4 ++-- .../defense_evasion_event_hub_deletion.json | 4 ++-- .../defense_evasion_firewall_policy_deletion.json | 4 ++-- ...efense_evasion_frontdoor_firewall_policy_deletion.json | 4 ++-- .../defense_evasion_gcp_firewall_rule_created.json | 5 +++-- .../defense_evasion_gcp_firewall_rule_deleted.json | 5 +++-- .../defense_evasion_gcp_firewall_rule_modified.json | 5 +++-- .../defense_evasion_gcp_logging_bucket_deletion.json | 5 +++-- .../defense_evasion_gcp_logging_sink_deletion.json | 5 +++-- ...defense_evasion_gcp_pub_sub_subscription_deletion.json | 5 +++-- .../defense_evasion_gcp_pub_sub_topic_deletion.json | 5 +++-- ...evasion_gcp_storage_bucket_configuration_modified.json | 5 +++-- ...e_evasion_gcp_storage_bucket_permissions_modified.json | 5 +++-- ...evasion_gcp_virtual_private_cloud_network_deleted.json | 5 +++-- ...e_evasion_gcp_virtual_private_cloud_route_created.json | 5 +++-- ...e_evasion_gcp_virtual_private_cloud_route_deleted.json | 5 +++-- ...asion_google_workspace_bitlocker_setting_disabled.json | 4 ++-- ...s_for_google_marketplace_changed_to_allow_any_app.json | 4 ++-- .../defense_evasion_guardduty_detector_deletion.json | 4 ++-- .../defense_evasion_kubernetes_events_deleted.json | 4 ++-- ...evasion_microsoft_365_exchange_dlp_policy_removed.json | 4 ++-- ...osoft_365_exchange_malware_filter_policy_deletion.json | 4 ++-- ...on_microsoft_365_exchange_malware_filter_rule_mod.json | 4 ++-- ..._microsoft_365_exchange_safe_attach_rule_disabled.json | 4 ++-- ...asion_microsoft_365_mailboxauditbypassassociation.json | 4 ++-- .../defense_evasion_network_watcher_deletion.json | 4 ++-- ...se_evasion_okta_attempt_to_deactivate_okta_policy.json | 4 ++-- ...asion_okta_attempt_to_deactivate_okta_policy_rule.json | 4 ++-- ...efense_evasion_okta_attempt_to_delete_okta_policy.json | 4 ++-- ...e_evasion_okta_attempt_to_delete_okta_policy_rule.json | 4 ++-- ..._evasion_okta_attempt_to_modify_okta_network_zone.json | 4 ++-- ...efense_evasion_okta_attempt_to_modify_okta_policy.json | 4 ++-- ...e_evasion_okta_attempt_to_modify_okta_policy_rule.json | 4 ++-- .../defense_evasion_s3_bucket_configuration_deletion.json | 4 ++-- .../defense_evasion_suppression_rule_created.json | 4 ++-- ...cious_okta_user_password_reset_or_unlock_attempts.json | 4 ++-- .../defense_evasion_waf_acl_deletion.json | 4 ++-- .../defense_evasion_waf_rule_or_rule_group_deletion.json | 4 ++-- .../discovery_blob_container_access_mod.json | 4 ++-- .../discovery_denied_service_account_request.json | 4 ++-- .../execution_command_virtual_machine.json | 4 ++-- ...ltration_ec2_full_network_packet_capture_detected.json | 4 ++-- .../exfiltration_ec2_snapshot_change_activity.json | 4 ++-- .../exfiltration_ec2_vm_export_failure.json | 4 ++-- .../exfiltration_gcp_logging_sink_modification.json | 5 +++-- ...on_microsoft_365_exchange_transport_rule_creation.json | 4 ++-- ...tration_microsoft_365_exchange_transport_rule_mod.json | 4 ++-- .../exfiltration_rds_snapshot_export.json | 4 ++-- .../exfiltration_rds_snapshot_restored.json | 4 ++-- .../impact_attempt_to_revoke_okta_api_token.json | 4 ++-- .../impact_aws_eventbridge_rule_disabled_or_deleted.json | 4 ++-- .../impact_azure_service_principal_credentials_added.json | 4 ++-- .../impact_cloudtrail_logging_updated.json | 4 ++-- .../impact_cloudwatch_log_group_deletion.json | 4 ++-- .../impact_cloudwatch_log_stream_deletion.json | 4 ++-- .../impact_ec2_disable_ebs_encryption.json | 4 ++-- .../impact_efs_filesystem_or_mount_deleted.json | 4 ++-- .../prepackaged_rules/impact_gcp_iam_role_deletion.json | 5 +++-- .../impact_gcp_service_account_deleted.json | 5 +++-- .../impact_gcp_service_account_disabled.json | 5 +++-- .../impact_gcp_storage_bucket_deleted.json | 5 +++-- .../impact_google_workspace_admin_role_deletion.json | 4 ++-- .../impact_google_workspace_mfa_enforcement_disabled.json | 4 ++-- .../impact_iam_deactivate_mfa_device.json | 4 ++-- .../prepackaged_rules/impact_iam_group_deletion.json | 4 ++-- .../prepackaged_rules/impact_kubernetes_pod_deleted.json | 4 ++-- ...mpact_microsoft_365_potential_ransomware_activity.json | 4 ++-- ...act_microsoft_365_unusual_volume_of_file_deletion.json | 4 ++-- ...mpact_okta_attempt_to_deactivate_okta_application.json | 4 ++-- .../impact_okta_attempt_to_delete_okta_application.json | 4 ++-- .../impact_okta_attempt_to_modify_okta_application.json | 4 ++-- .../impact_possible_okta_dos_attack.json | 4 ++-- .../prepackaged_rules/impact_rds_group_deletion.json | 4 ++-- .../impact_rds_instance_cluster_deletion.json | 4 ++-- .../impact_rds_instance_cluster_stoppage.json | 4 ++-- .../prepackaged_rules/impact_resource_group_deletion.json | 4 ++-- .../impact_virtual_network_device_modified.json | 4 ++-- ...al_access_azure_active_directory_high_risk_signin.json | 4 ++-- ...ve_directory_high_risk_signin_atrisk_or_confirmed.json | 4 ++-- ...l_access_azure_active_directory_powershell_signin.json | 4 ++-- ...ent_grant_attack_via_azure_registered_application.json | 8 ++++---- .../initial_access_console_login_root.json | 4 ++-- .../initial_access_external_guest_user_invite.json | 4 ++-- .../initial_access_gcp_iam_custom_role_creation.json | 5 +++-- ...microsoft_365_exchange_anti_phish_policy_deletion.json | 4 ++-- ...access_microsoft_365_exchange_anti_phish_rule_mod.json | 4 ++-- ..._access_microsoft_365_exchange_safelinks_disabled.json | 4 ++-- ..._microsoft_365_user_restricted_from_sending_email.json | 4 ++-- .../initial_access_o365_user_reported_phish_malware.json | 4 ++-- ...al_access_okta_user_attempted_unauthorized_access.json | 4 ++-- .../initial_access_password_recovery.json | 4 ++-- ..._access_suspicious_activity_reported_by_okta_user.json | 4 ++-- .../initial_access_via_system_manager.json | 4 ++-- .../lateral_movement_malware_uploaded_onedrive.json | 4 ++-- .../lateral_movement_malware_uploaded_sharepoint.json | 4 ++-- .../okta_threat_detected_by_okta_threatinsight.json | 4 ++-- ...e_administrator_privileges_assigned_to_okta_group.json | 4 ++-- ...sistence_administrator_role_assigned_to_okta_user.json | 4 ++-- ...ence_application_added_to_google_workspace_domain.json | 4 ++-- .../persistence_attempt_to_create_okta_api_token.json | 4 ++-- ...e_attempt_to_deactivate_mfa_for_okta_user_account.json | 4 ++-- ...ttempt_to_reset_mfa_factors_for_okta_user_account.json | 4 ++-- .../persistence_azure_automation_account_created.json | 4 ++-- ...ence_azure_automation_runbook_created_or_modified.json | 4 ++-- .../persistence_azure_automation_webhook_created.json | 4 ++-- ...sistence_azure_conditional_access_policy_modified.json | 6 +++--- ...sistence_azure_global_administrator_role_assigned.json | 4 ++-- .../persistence_azure_pim_user_added_global_admin.json | 4 ++-- ...zure_privileged_identity_management_role_modified.json | 4 ++-- .../persistence_ec2_network_acl_creation.json | 4 ++-- ...ec2_security_group_configuration_change_detection.json | 4 ++-- ...ence_exchange_suspicious_mailbox_right_delegation.json | 4 ++-- .../persistence_gcp_iam_service_account_key_deletion.json | 5 +++-- .../persistence_gcp_key_created_for_service_account.json | 5 +++-- .../persistence_gcp_service_account_created.json | 5 +++-- .../persistence_google_workspace_2sv_policy_disabled.json | 4 ++-- ...ence_google_workspace_admin_role_assigned_to_user.json | 4 ++-- ...s_granted_via_domain_wide_delegation_of_authority.json | 4 ++-- ...stence_google_workspace_custom_admin_role_created.json | 4 ++-- .../persistence_google_workspace_policy_modified.json | 4 ++-- .../persistence_google_workspace_role_modified.json | 4 ++-- ...er_group_access_modified_to_allow_external_access.json | 4 ++-- ...google_workspace_user_organizational_unit_changed.json | 4 ++-- .../prepackaged_rules/persistence_iam_group_creation.json | 4 ++-- .../persistence_mfa_disabled_for_azure_user.json | 4 ++-- ...ce_mfa_disabled_for_google_workspace_organization.json | 4 ++-- ...crosoft_365_exchange_dkim_signing_config_disabled.json | 4 ++-- ...microsoft_365_exchange_management_role_assignment.json | 4 ++-- ...ce_microsoft_365_global_administrator_role_assign.json | 4 ++-- ...icrosoft_365_teams_custom_app_interaction_allowed.json | 4 ++-- ...tence_microsoft_365_teams_external_access_enabled.json | 4 ++-- ...sistence_microsoft_365_teams_guest_access_enabled.json | 4 ++-- ...pt_to_modify_or_delete_application_sign_on_policy.json | 4 ++-- .../persistence_rds_cluster_creation.json | 4 ++-- .../prepackaged_rules/persistence_rds_group_creation.json | 4 ++-- .../persistence_rds_instance_creation.json | 4 ++-- .../persistence_redshift_instance_creation.json | 4 ++-- ...ersistence_route_53_domain_transfer_lock_disabled.json | 4 ++-- ...ce_route_53_domain_transferred_to_another_account.json | 4 ++-- ...stence_route_53_hosted_zone_associated_with_a_vpc.json | 4 ++-- .../persistence_route_table_created.json | 4 ++-- .../persistence_route_table_modified_or_deleted.json | 4 ++-- ...istence_user_added_as_owner_for_azure_application.json | 4 ++-- ...e_user_added_as_owner_for_azure_service_principal.json | 4 ++-- ...privilege_escalation_aws_suspicious_saml_activity.json | 4 ++-- ...e_escalation_azure_kubernetes_rolebinding_created.json | 4 ++-- ...scalation_cyberarkpas_error_audit_event_promotion.json | 4 ++-- ...berarkpas_recommended_events_to_monitor_promotion.json | 4 ++-- ...on_gcp_kubernetes_rolebindings_created_or_patched.json | 5 +++-- ...lege_escalation_new_or_modified_federation_domain.json | 4 ++-- .../privilege_escalation_root_login_without_mfa.json | 4 ++-- .../privilege_escalation_sts_assumerole_usage.json | 4 ++-- .../privilege_escalation_sts_getsessiontoken_abuse.json | 4 ++-- ...spicious_assignment_of_controller_service_account.json | 4 ++-- .../privilege_escalation_updateassumerolepolicy.json | 4 ++-- 193 files changed, 413 insertions(+), 389 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json index dce3794eaa27..1c18ffdb366a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_subscription_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_subscription_creation.json index 170799f184f4..bde0e521ccc8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_subscription_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_subscription_creation.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_topic_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_topic_creation.json index 0a81f7147a6f..374cd565a4cd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_topic_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_gcp_pub_sub_topic_creation.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_drive_ownership_transferred_via_google_workspace.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_drive_ownership_transferred_via_google_workspace.json index 7786d21c92e1..84b930fc30b3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_drive_ownership_transferred_via_google_workspace.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_drive_ownership_transferred_via_google_workspace.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -86,5 +86,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_workspace_custom_gmail_route_created_or_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_workspace_custom_gmail_route_created_or_modified.json index ecf97a03e224..f6c591d8922d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_workspace_custom_gmail_route_created_or_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_google_workspace_custom_gmail_route_created_or_modified.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -86,5 +86,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json index 2872c1aa509e..ef67f834a05e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json @@ -27,7 +27,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -110,5 +110,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_update_event_hub_auth_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_update_event_hub_auth_rule.json index f3858c18a6e2..ab64a125a544 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_update_event_hub_auth_rule.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_update_event_hub_auth_rule.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempted_bypass_of_okta_mfa.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempted_bypass_of_okta_mfa.json index cdc1899aef25..854e7c05f1ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempted_bypass_of_okta_mfa.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempted_bypass_of_okta_mfa.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -65,5 +65,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempts_to_brute_force_okta_user_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempts_to_brute_force_okta_user_account.json index 5a51259d349a..1e0e64288a25 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempts_to_brute_force_okta_user_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_attempts_to_brute_force_okta_user_account.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -73,5 +73,5 @@ "value": 3 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_aws_iam_assume_role_brute_force.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_aws_iam_assume_role_brute_force.json index 490b96665a83..1ecaf2dd8bfd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_aws_iam_assume_role_brute_force.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_aws_iam_assume_role_brute_force.json @@ -21,7 +21,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -86,5 +86,5 @@ "value": 25 }, "type": "threshold", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_azure_full_network_packet_capture_detected.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_azure_full_network_packet_capture_detected.json index 7ecff1cab054..0424bd725423 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_azure_full_network_packet_capture_detected.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_azure_full_network_packet_capture_detected.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -74,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json index 2140870e22d1..70cfa845453b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_key_vault_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_key_vault_modified.json index b4cb0de511fa..bda2c0ad53d2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_key_vault_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_key_vault_modified.json @@ -24,7 +24,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_brute_force_user_account_attempt.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_brute_force_user_account_attempt.json index b14caa11172a..e4d55e511c98 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_brute_force_user_account_attempt.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_brute_force_user_account_attempt.json @@ -24,7 +24,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -90,5 +90,5 @@ "value": 10 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_potential_password_spraying_attack.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_potential_password_spraying_attack.json index 58dcd2838e88..25fbb268525b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_potential_password_spraying_attack.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_microsoft_365_potential_password_spraying_attack.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -80,5 +80,5 @@ "value": 25 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_okta_brute_force_or_password_spraying.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_okta_brute_force_or_password_spraying.json index a9838a1be2f7..a5a2a5c4b5f6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_okta_brute_force_or_password_spraying.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_okta_brute_force_or_password_spraying.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ "value": 25 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_root_console_failure_brute_force.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_root_console_failure_brute_force.json index fa80873d0029..e28873764450 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_root_console_failure_brute_force.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_root_console_failure_brute_force.json @@ -23,7 +23,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ "value": 10 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_secretsmanager_getsecretvalue.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_secretsmanager_getsecretvalue.json index 2ec5b4d9a877..e48cd55b3dc3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_secretsmanager_getsecretvalue.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_secretsmanager_getsecretvalue.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -79,5 +79,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_storage_account_key_regenerated.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_storage_account_key_regenerated.json index a8f3ebcb02bd..0b21f4e30fe2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_storage_account_key_regenerated.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_storage_account_key_regenerated.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -74,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_excessive_sso_logon_errors.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_excessive_sso_logon_errors.json index 08ce349cf9a2..1fe876087598 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_excessive_sso_logon_errors.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_excessive_sso_logon_errors.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ "value": 5 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_impersonation_access.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_impersonation_access.json index c81d9e99eb40..db1954bf32d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_impersonation_access.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_user_impersonation_access.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -61,5 +61,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_application_removed_from_blocklist_in_google_workspace.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_application_removed_from_blocklist_in_google_workspace.json index 42cb730b4bd5..24fbf972f262 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_application_removed_from_blocklist_in_google_workspace.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_application_removed_from_blocklist_in_google_workspace.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -102,5 +102,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_deactivate_okta_network_zone.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_deactivate_okta_network_zone.json index 5a369c99e1e1..48b117098e89 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_deactivate_okta_network_zone.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_deactivate_okta_network_zone.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_delete_okta_network_zone.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_delete_okta_network_zone.json index df742d5e50db..a94413ddbd69 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_delete_okta_network_zone.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_attempt_to_delete_okta_network_zone.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_application_credential_modification.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_application_credential_modification.json index 430a6e5f760c..15d9cfc43b4f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_application_credential_modification.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_application_credential_modification.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -80,5 +80,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_automation_runbook_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_automation_runbook_deleted.json index 2b85723e10a1..ea752fb3e753 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_automation_runbook_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_automation_runbook_deleted.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -69,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_blob_permissions_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_blob_permissions_modified.json index 09c799005d1c..d7d398b93b8c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_blob_permissions_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_blob_permissions_modified.json @@ -22,7 +22,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -73,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_diagnostic_settings_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_diagnostic_settings_deletion.json index 050a8949db39..391c7550cebe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_diagnostic_settings_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_diagnostic_settings_deletion.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_service_principal_addition.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_service_principal_addition.json index 8b409bdb6368..23ffd879c111 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_service_principal_addition.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_azure_service_principal_addition.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json index cb0f9d549a04..c9612870bcdd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json index 97867f74d055..abe9271b8301 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json index 6fc0a85d8e9c..b6945f8b9f10 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json index 9113f5907dd3..a60d5015f4c6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -85,5 +85,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json index 6677ce569b49..17cd9c6e1600 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_domain_added_to_google_workspace_trusted_domains.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_domain_added_to_google_workspace_trusted_domains.json index 75bbc1e8b334..178c5c42f0d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_domain_added_to_google_workspace_trusted_domains.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_domain_added_to_google_workspace_trusted_domains.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json index 9104509c8576..1bfe183a5a45 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json index 4eb08d21f5e6..b0ec864e4085 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json @@ -27,7 +27,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -90,5 +90,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_creation.json index 9acd6f767b61..405215618b17 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_creation.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_modified_or_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_modified_or_deleted.json index 8548f32e06a0..909a8474c208 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_modified_or_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_elasticache_security_group_modified_or_deleted.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_event_hub_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_event_hub_deletion.json index 36545e9bff9e..5f52bf06a19d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_event_hub_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_event_hub_deletion.json @@ -25,7 +25,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_firewall_policy_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_firewall_policy_deletion.json index f1e0e99c67ec..d0ffb0a05279 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_firewall_policy_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_firewall_policy_deletion.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_frontdoor_firewall_policy_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_frontdoor_firewall_policy_deletion.json index 4269f229a2ae..8c5cfff14d6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_frontdoor_firewall_policy_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_frontdoor_firewall_policy_deletion.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_created.json index 93ca073a171a..3c9adaefa823 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_created.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -68,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_deleted.json index d49edc4f123b..f3debd68ebe0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_deleted.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -68,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_modified.json index 3b0ca907a7e4..da1e2ccb8925 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_firewall_rule_modified.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -68,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_bucket_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_bucket_deletion.json index 41e6366756a0..8f93b61c574a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_bucket_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_bucket_deletion.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -73,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_sink_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_sink_deletion.json index b93701685a11..781bc1d2e9be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_sink_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_logging_sink_deletion.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_subscription_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_subscription_deletion.json index bf1890f548c5..32e148dc25d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_subscription_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_subscription_deletion.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_topic_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_topic_deletion.json index 283b83874d89..eee336c3d9b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_topic_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_pub_sub_topic_deletion.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json index e0470117bd11..1bb6d123de88 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -73,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json index f8f9cfad37be..2a334c80ab68 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_network_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_network_deleted.json index 76b058e65a1c..54cb615d7156 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_network_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_network_deleted.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -80,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_created.json index c4f344846bd8..5926141fdabb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_created.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -76,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_deleted.json index e2b483fc9298..ab75552f7a06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_virtual_private_cloud_route_deleted.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -81,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_bitlocker_setting_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_bitlocker_setting_disabled.json index 018c8d0c9d6d..8c0b5fbbb435 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_bitlocker_setting_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_bitlocker_setting_disabled.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_restrictions_for_google_marketplace_changed_to_allow_any_app.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_restrictions_for_google_marketplace_changed_to_allow_any_app.json index ddb123498fa8..d69005491d5e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_restrictions_for_google_marketplace_changed_to_allow_any_app.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_google_workspace_restrictions_for_google_marketplace_changed_to_allow_any_app.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -102,5 +102,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json index 5186e952ab84..dab57a174fc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_kubernetes_events_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_kubernetes_events_deleted.json index af1c51fa0a15..c4f932ad4f6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_kubernetes_events_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_kubernetes_events_deleted.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_dlp_policy_removed.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_dlp_policy_removed.json index bc5cc6202093..bc1faac1570e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_dlp_policy_removed.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_dlp_policy_removed.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json index 63901b5d86aa..5f5974ad79b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json index 3845a4e1c9a1..92d1e2ee110f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json index 8847290e99c8..098fc20b7dd0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_mailboxauditbypassassociation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_mailboxauditbypassassociation.json index dfff4d997635..7896aa38be70 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_mailboxauditbypassassociation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_microsoft_365_mailboxauditbypassassociation.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -85,5 +85,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_network_watcher_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_network_watcher_deletion.json index 167baa27c2fe..7124819843f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_network_watcher_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_network_watcher_deletion.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy.json index e988bc58b4fe..c615e34089ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy_rule.json index 4ba9330f4a47..81d8782e6ce5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy_rule.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_deactivate_okta_policy_rule.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy.json index 040e7791483e..86cd009618c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy_rule.json index 7cdd8c780f78..295fb493e6ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy_rule.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_delete_okta_policy_rule.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_network_zone.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_network_zone.json index f3a771f184e6..15a51b88c097 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_network_zone.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_network_zone.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy.json index 199d7de64979..1a69f862b695 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -76,5 +76,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy_rule.json index 17aeae26a983..358419263a30 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy_rule.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_okta_attempt_to_modify_okta_policy_rule.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -77,5 +77,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json index 1cada93687ba..b61983f423a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json @@ -28,7 +28,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suppression_rule_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suppression_rule_created.json index 493e8dbbd060..139e05bebcc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suppression_rule_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suppression_rule_created.json @@ -24,7 +24,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -75,5 +75,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json index 72a518d5542e..f537c79c427e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json @@ -25,7 +25,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -106,5 +106,5 @@ "value": 5 }, "type": "threshold", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json index 378a43273394..9f34837071f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_rule_or_rule_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_rule_or_rule_group_deletion.json index b1e136776c8d..59eb206ec6bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_rule_or_rule_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_rule_or_rule_group_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_blob_container_access_mod.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_blob_container_access_mod.json index 59c500d79837..42671c621d45 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_blob_container_access_mod.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_blob_container_access_mod.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_denied_service_account_request.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_denied_service_account_request.json index 84cafb61d094..829a7a90c422 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_denied_service_account_request.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_denied_service_account_request.json @@ -21,7 +21,7 @@ "related_integrations": [ { "package": "kubernetes", - "version": "1.17.2" + "version": "^1.4.1" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_virtual_machine.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_virtual_machine.json index 1d4397fbf23d..badcfa517ab2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_virtual_machine.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_virtual_machine.json @@ -25,7 +25,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -76,5 +76,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_full_network_packet_capture_detected.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_full_network_packet_capture_detected.json index 12b267935e02..a4bda89c20be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_full_network_packet_capture_detected.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_full_network_packet_capture_detected.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -97,5 +97,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_snapshot_change_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_snapshot_change_activity.json index d8de82c5251a..62f31b39b962 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_snapshot_change_activity.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_snapshot_change_activity.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_vm_export_failure.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_vm_export_failure.json index f8f9895aa991..c0d9a50a8a5c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_vm_export_failure.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_ec2_vm_export_failure.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -96,5 +96,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_gcp_logging_sink_modification.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_gcp_logging_sink_modification.json index 00154239c029..dd7a0d6c5926 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_gcp_logging_sink_modification.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_gcp_logging_sink_modification.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_creation.json index 0017b57c6dbc..62cffaebe5b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_creation.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_mod.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_mod.json index 5931b6c32f0a..4f6ac0d2e6e9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_mod.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_microsoft_365_exchange_transport_rule_mod.json @@ -24,7 +24,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -85,5 +85,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_export.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_export.json index 3e344f4a0b87..2da17b0259f6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_export.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_export.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -76,5 +76,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_restored.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_restored.json index 264907bd84b6..fc9a4124e2d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_restored.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/exfiltration_rds_snapshot_restored.json @@ -23,7 +23,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_attempt_to_revoke_okta_api_token.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_attempt_to_revoke_okta_api_token.json index 59a472b0713d..0c2c25e53bc3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_attempt_to_revoke_okta_api_token.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_attempt_to_revoke_okta_api_token.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_aws_eventbridge_rule_disabled_or_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_aws_eventbridge_rule_disabled_or_deleted.json index 888b6b54cb17..a8cf9c9f21d7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_aws_eventbridge_rule_disabled_or_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_aws_eventbridge_rule_disabled_or_deleted.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_azure_service_principal_credentials_added.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_azure_service_principal_credentials_added.json index 584f87b741a2..93281b57f429 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_azure_service_principal_credentials_added.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_azure_service_principal_credentials_added.json @@ -24,7 +24,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -75,5 +75,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json index 80ad1350df65..a46154a969ef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -104,5 +104,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json index 5734ffe4c312..b0519a0afa47 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -104,5 +104,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json index 04870a18138e..3b188fb8a1ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -105,5 +105,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json index 6f280977acbe..78989e7934a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_efs_filesystem_or_mount_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_efs_filesystem_or_mount_deleted.json index c581b4cd72b6..1ba9aa185a72 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_efs_filesystem_or_mount_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_efs_filesystem_or_mount_deleted.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_iam_role_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_iam_role_deletion.json index fe9dec0ced23..4f6e6aea313a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_iam_role_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_iam_role_deletion.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_deleted.json index e8a6f0892257..640c048d79ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_deleted.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_disabled.json index 23531bad359b..f9a09d4eb6e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_service_account_disabled.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json index ec518694a254..8648402625a3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -67,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_admin_role_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_admin_role_deletion.json index 735baec178a6..791efdc6463e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_admin_role_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_admin_role_deletion.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -80,5 +80,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_mfa_enforcement_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_mfa_enforcement_disabled.json index 6095c3c3a392..cf2dd80c5fd3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_mfa_enforcement_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_google_workspace_mfa_enforcement_disabled.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -86,5 +86,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json index 49c4f3255155..99bb18ad4356 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json index e5b9b9d2b3d0..f16bdaa569c1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_kubernetes_pod_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_kubernetes_pod_deleted.json index 79792cad4e96..0c8c3ab14c16 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_kubernetes_pod_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_kubernetes_pod_deleted.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_potential_ransomware_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_potential_ransomware_activity.json index 1419e6c36cd2..ae82beafce00 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_potential_ransomware_activity.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_potential_ransomware_activity.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_unusual_volume_of_file_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_unusual_volume_of_file_deletion.json index b572bfdbbaef..0954bde76504 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_unusual_volume_of_file_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_microsoft_365_unusual_volume_of_file_deletion.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_deactivate_okta_application.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_deactivate_okta_application.json index 70551d2c173d..790e19aaf182 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_deactivate_okta_application.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_deactivate_okta_application.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_delete_okta_application.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_delete_okta_application.json index bf7c616bb33f..c816403377b7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_delete_okta_application.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_delete_okta_application.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -69,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_modify_okta_application.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_modify_okta_application.json index 853116822a34..c6fdccd64039 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_modify_okta_application.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_okta_attempt_to_modify_okta_application.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -64,5 +64,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_possible_okta_dos_attack.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_possible_okta_dos_attack.json index 85f7502f26e0..7fc0083fc13c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_possible_okta_dos_attack.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_possible_okta_dos_attack.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_group_deletion.json index 155afc369af2..9c7dcff92bb1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_group_deletion.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_deletion.json index 18b277adc56d..bb3527fac3d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_deletion.json @@ -29,7 +29,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -85,5 +85,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json index 15e02efcb8e4..dc5ce6e246bf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json @@ -27,7 +27,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_resource_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_resource_group_deletion.json index 453c189f75d1..f7603f5b2f8e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_resource_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_resource_group_deletion.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -96,5 +96,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_virtual_network_device_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_virtual_network_device_modified.json index d090ae229add..bf74fe3509c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_virtual_network_device_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_virtual_network_device_modified.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -69,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin.json index d06a073fd504..e92a0cc3ea63 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -79,5 +79,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin_atrisk_or_confirmed.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin_atrisk_or_confirmed.json index c2d57924a39e..7d39ad47973d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin_atrisk_or_confirmed.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_high_risk_signin_atrisk_or_confirmed.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -74,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_powershell_signin.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_powershell_signin.json index d46d34a762cc..4ea3feedaa35 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_powershell_signin.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_azure_active_directory_powershell_signin.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_consent_grant_attack_via_azure_registered_application.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_consent_grant_attack_via_azure_registered_application.json index 01a68b944d29..8828b5037ca2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_consent_grant_attack_via_azure_registered_application.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_consent_grant_attack_via_azure_registered_application.json @@ -22,15 +22,15 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" }, { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" }, { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -114,5 +114,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json index 7612d79572c1..929d34100180 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -101,5 +101,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_external_guest_user_invite.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_external_guest_user_invite.json index 159e0bd1b9e1..c100bcc9bc97 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_external_guest_user_invite.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_external_guest_user_invite.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -93,5 +93,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_gcp_iam_custom_role_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_gcp_iam_custom_role_creation.json index ad92d1b3498e..eebc12c09215 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_gcp_iam_custom_role_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_gcp_iam_custom_role_creation.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -87,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json index 94ad9f0b4bb8..f071d0121932 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_rule_mod.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_rule_mod.json index 7d8a1b23585e..f9ff2bdd02b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_rule_mod.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_anti_phish_rule_mod.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_safelinks_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_safelinks_disabled.json index ac24c0321f6e..a69b1acdaff7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_safelinks_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_exchange_safelinks_disabled.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_user_restricted_from_sending_email.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_user_restricted_from_sending_email.json index 34722a9bed96..a6b58f03c5bf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_user_restricted_from_sending_email.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_microsoft_365_user_restricted_from_sending_email.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_o365_user_reported_phish_malware.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_o365_user_reported_phish_malware.json index 64e64c1d919d..b72807030884 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_o365_user_reported_phish_malware.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_o365_user_reported_phish_malware.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -90,5 +90,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_okta_user_attempted_unauthorized_access.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_okta_user_attempted_unauthorized_access.json index 33d212c9c5e9..77a7e0080cab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_okta_user_attempted_unauthorized_access.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_okta_user_attempted_unauthorized_access.json @@ -16,7 +16,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json index 2e0c53cead87..cfade77b3e42 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -80,5 +80,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_activity_reported_by_okta_user.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_activity_reported_by_okta_user.json index cf7557102e20..ce6ef31fa4a1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_activity_reported_by_okta_user.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_activity_reported_by_okta_user.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -113,5 +113,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_system_manager.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_system_manager.json index 78fea7b5dafe..2cc55e2b14ff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_system_manager.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_system_manager.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_onedrive.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_onedrive.json index 299fba09b77f..86093446957e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_onedrive.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_onedrive.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_sharepoint.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_sharepoint.json index 1cbed1359e9e..021614c0631c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_sharepoint.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_malware_uploaded_sharepoint.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/okta_threat_detected_by_okta_threatinsight.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/okta_threat_detected_by_okta_threatinsight.json index 71f2addd91d8..18fe87682f81 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/okta_threat_detected_by_okta_threatinsight.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/okta_threat_detected_by_okta_threatinsight.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -48,5 +48,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_privileges_assigned_to_okta_group.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_privileges_assigned_to_okta_group.json index e903dce0ff46..be6ab836b1c0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_privileges_assigned_to_okta_group.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_privileges_assigned_to_okta_group.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -69,5 +69,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_role_assigned_to_okta_user.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_role_assigned_to_okta_user.json index 148ba8c09e88..4d22e033ac12 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_role_assigned_to_okta_user.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_administrator_role_assigned_to_okta_user.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_application_added_to_google_workspace_domain.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_application_added_to_google_workspace_domain.json index 34870bf55be7..bd21d631ba81 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_application_added_to_google_workspace_domain.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_application_added_to_google_workspace_domain.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -74,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_create_okta_api_token.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_create_okta_api_token.json index e1eec9d4e590..66147953d146 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_create_okta_api_token.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_create_okta_api_token.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_deactivate_mfa_for_okta_user_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_deactivate_mfa_for_okta_user_account.json index a5b8762ec1aa..c0338b37b53e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_deactivate_mfa_for_okta_user_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_deactivate_mfa_for_okta_user_account.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json index 585b74cb73e4..0d230518055f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_account_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_account_created.json index ccbf96ab79d0..af6d6bfcb2c5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_account_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_account_created.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_runbook_created_or_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_runbook_created_or_modified.json index 7518dcdf9a5f..1fbf08e86b14 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_runbook_created_or_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_runbook_created_or_modified.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -57,5 +57,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json index a7a9e7ad8f2d..51088c01c08e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json @@ -23,7 +23,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -57,5 +57,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_conditional_access_policy_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_conditional_access_policy_modified.json index e2f81083938b..68ba96820751 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_conditional_access_policy_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_conditional_access_policy_modified.json @@ -20,11 +20,11 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" }, { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -75,5 +75,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_global_administrator_role_assigned.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_global_administrator_role_assigned.json index 91c5a8b9bc20..88329c459385 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_global_administrator_role_assigned.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_global_administrator_role_assigned.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_pim_user_added_global_admin.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_pim_user_added_global_admin.json index 7dfc8b953a4b..fddf8ca8bed6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_pim_user_added_global_admin.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_pim_user_added_global_admin.json @@ -21,7 +21,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_privileged_identity_management_role_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_privileged_identity_management_role_modified.json index 80720e7aff6c..fcda83a95bed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_privileged_identity_management_role_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_privileged_identity_management_role_modified.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json index f510200c25a6..f083d6bd84e5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json @@ -27,7 +27,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_security_group_configuration_change_detection.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_security_group_configuration_change_detection.json index 4d33c73f6bbe..69734f6c2ff4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_security_group_configuration_change_detection.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_security_group_configuration_change_detection.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -97,5 +97,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_exchange_suspicious_mailbox_right_delegation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_exchange_suspicious_mailbox_right_delegation.json index 0dddfad51753..b1c3453554cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_exchange_suspicious_mailbox_right_delegation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_exchange_suspicious_mailbox_right_delegation.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_iam_service_account_key_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_iam_service_account_key_deletion.json index 3376e777cf48..0d9d6775ef00 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_iam_service_account_key_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_iam_service_account_key_deletion.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -73,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_key_created_for_service_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_key_created_for_service_account.json index 2c39c99f5512..69728373e6c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_key_created_for_service_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_key_created_for_service_account.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -73,5 +74,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_service_account_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_service_account_created.json index c9aaa61d367e..d30dabdd112d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_service_account_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gcp_service_account_created.json @@ -20,8 +20,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_2sv_policy_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_2sv_policy_disabled.json index be09aceb67c9..a2b738be6655 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_2sv_policy_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_2sv_policy_disabled.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_admin_role_assigned_to_user.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_admin_role_assigned_to_user.json index 86825d008eea..9eba65a1e99d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_admin_role_assigned_to_user.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_admin_role_assigned_to_user.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json index 3b828c3b868f..b1d854bc7402 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -79,5 +79,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_custom_admin_role_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_custom_admin_role_created.json index e25ef8e452d7..775470992ae5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_custom_admin_role_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_custom_admin_role_created.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -79,5 +79,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_policy_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_policy_modified.json index 7e1366d93687..b14935910b06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_policy_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_policy_modified.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_role_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_role_modified.json index b9f1fe4cbd7c..bff307fea840 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_role_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_role_modified.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -79,5 +79,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_group_access_modified_to_allow_external_access.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_group_access_modified_to_allow_external_access.json index 435ad2fdc432..fdd4c46e53a2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_group_access_modified_to_allow_external_access.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_group_access_modified_to_allow_external_access.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_organizational_unit_changed.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_organizational_unit_changed.json index e843d62e1301..76e89a5d4717 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_organizational_unit_changed.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_google_workspace_user_organizational_unit_changed.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json index 09d17c5f088d..d23c3db3c249 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_azure_user.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_azure_user.json index 8b21e5358f71..b1cabdf2dc00 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_azure_user.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_azure_user.json @@ -16,7 +16,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -68,5 +68,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_google_workspace_organization.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_google_workspace_organization.json index ea562c183756..b5dde6769661 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_google_workspace_organization.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_mfa_disabled_for_google_workspace_organization.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "google_workspace", - "version": "1.2.0" + "version": "^1.2.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_dkim_signing_config_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_dkim_signing_config_disabled.json index 1acb6b4097ab..034dd2cca73e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_dkim_signing_config_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_dkim_signing_config_disabled.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -89,5 +89,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_management_role_assignment.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_management_role_assignment.json index 633c904fe746..648c9fb76e85 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_management_role_assignment.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_exchange_management_role_assignment.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -84,5 +84,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_global_administrator_role_assign.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_global_administrator_role_assign.json index 8ff2741303bd..d9878e325c37 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_global_administrator_role_assign.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_global_administrator_role_assign.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_custom_app_interaction_allowed.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_custom_app_interaction_allowed.json index 485c55e14f22..3535cad54764 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_custom_app_interaction_allowed.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_custom_app_interaction_allowed.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_external_access_enabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_external_access_enabled.json index 6297c5b5ca25..05e60d247053 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_external_access_enabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_external_access_enabled.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_guest_access_enabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_guest_access_enabled.json index 6767d2da121c..d102a801655e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_guest_access_enabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_microsoft_365_teams_guest_access_enabled.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_okta_attempt_to_modify_or_delete_application_sign_on_policy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_okta_attempt_to_modify_or_delete_application_sign_on_policy.json index 177f4f5c09e9..c3de28469170 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_okta_attempt_to_modify_or_delete_application_sign_on_policy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_okta_attempt_to_modify_or_delete_application_sign_on_policy.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "okta", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json index 13c04ef97a79..6cd477633753 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json @@ -27,7 +27,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -92,5 +92,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_group_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_group_creation.json index 8c880e9dcb09..b0193afd93c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_group_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_group_creation.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -88,5 +88,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_instance_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_instance_creation.json index 49487238f21f..678d0feea17b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_instance_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_instance_creation.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -76,5 +76,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_redshift_instance_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_redshift_instance_creation.json index 3283c14d9aca..6375be698d4a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_redshift_instance_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_redshift_instance_creation.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -75,5 +75,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transfer_lock_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transfer_lock_disabled.json index 8dca090e0f9a..2110c9652e3b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transfer_lock_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transfer_lock_disabled.json @@ -26,7 +26,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -91,5 +91,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transferred_to_another_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transferred_to_another_account.json index c62d4384302e..05ce6140607c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transferred_to_another_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_domain_transferred_to_another_account.json @@ -25,7 +25,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -90,5 +90,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_hosted_zone_associated_with_a_vpc.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_hosted_zone_associated_with_a_vpc.json index 43d7e004867d..03e739c86e57 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_hosted_zone_associated_with_a_vpc.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_53_hosted_zone_associated_with_a_vpc.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -80,5 +80,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_created.json index 49ed26fdd2d4..2f5858bf331e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_created.json @@ -27,7 +27,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_modified_or_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_modified_or_deleted.json index 9855381acc3a..69c6f31579b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_modified_or_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_route_table_modified_or_deleted.json @@ -31,7 +31,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -82,5 +82,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_application.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_application.json index 4fb01f45f6d5..a7f57e93cbaf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_application.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_application.json @@ -16,7 +16,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -67,5 +67,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_service_principal.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_service_principal.json index ed398ed5e9ea..066b0cbfe8d8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_service_principal.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_added_as_owner_for_azure_service_principal.json @@ -19,7 +19,7 @@ "related_integrations": [ { "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -70,5 +70,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_aws_suspicious_saml_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_aws_suspicious_saml_activity.json index 21015597f946..e8119d810dbc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_aws_suspicious_saml_activity.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_aws_suspicious_saml_activity.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -102,5 +102,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_azure_kubernetes_rolebinding_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_azure_kubernetes_rolebinding_created.json index deed8fb00637..23173232a65d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_azure_kubernetes_rolebinding_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_azure_kubernetes_rolebinding_created.json @@ -21,7 +21,7 @@ { "integration": "activitylogs", "package": "azure", - "version": "0.12.0" + "version": "^1.0.0" } ], "required_fields": [ @@ -66,5 +66,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_error_audit_event_promotion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_error_audit_event_promotion.json index bb5dbe031c7d..3484db354a1d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_error_audit_event_promotion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_error_audit_event_promotion.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "cyberarkpas", - "version": "2.2.0" + "version": "^2.2.0" } ], "required_fields": [ @@ -78,5 +78,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_recommended_events_to_monitor_promotion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_recommended_events_to_monitor_promotion.json index 71d918844b94..6ee8411d989f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_recommended_events_to_monitor_promotion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_cyberarkpas_recommended_events_to_monitor_promotion.json @@ -22,7 +22,7 @@ "related_integrations": [ { "package": "cyberarkpas", - "version": "2.2.0" + "version": "^2.2.0" } ], "required_fields": [ @@ -83,5 +83,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_gcp_kubernetes_rolebindings_created_or_patched.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_gcp_kubernetes_rolebindings_created_or_patched.json index 1f949b57edc1..41d7c2e58474 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_gcp_kubernetes_rolebindings_created_or_patched.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_gcp_kubernetes_rolebindings_created_or_patched.json @@ -21,8 +21,9 @@ ], "related_integrations": [ { + "integration": "audit", "package": "gcp", - "version": "1.10.0" + "version": "^2.2.1" } ], "required_fields": [ @@ -72,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_new_or_modified_federation_domain.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_new_or_modified_federation_domain.json index 90601a72b1b3..fba42b17b4fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_new_or_modified_federation_domain.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_new_or_modified_federation_domain.json @@ -23,7 +23,7 @@ "related_integrations": [ { "package": "o365", - "version": "1.3.0" + "version": "^1.3.0" } ], "required_fields": [ @@ -91,5 +91,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_root_login_without_mfa.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_root_login_without_mfa.json index 01b6ebd88018..058ff41c7444 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_root_login_without_mfa.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_root_login_without_mfa.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -91,5 +91,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_assumerole_usage.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_assumerole_usage.json index d6acdcbc3d06..b90ac6954163 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_assumerole_usage.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_assumerole_usage.json @@ -22,7 +22,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -105,5 +105,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_getsessiontoken_abuse.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_getsessiontoken_abuse.json index f0a963883b26..588708bcef4c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_getsessiontoken_abuse.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_sts_getsessiontoken_abuse.json @@ -22,7 +22,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -105,5 +105,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 100 + "version": 101 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_suspicious_assignment_of_controller_service_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_suspicious_assignment_of_controller_service_account.json index 9673e704e477..fa217cf1cfd8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_suspicious_assignment_of_controller_service_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_suspicious_assignment_of_controller_service_account.json @@ -20,7 +20,7 @@ "related_integrations": [ { "package": "kubernetes", - "version": "1.17.2" + "version": "^1.4.1" } ], "required_fields": [ @@ -87,5 +87,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_updateassumerolepolicy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_updateassumerolepolicy.json index b632697515ad..6e6bbe4bdef1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_updateassumerolepolicy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_updateassumerolepolicy.json @@ -24,7 +24,7 @@ { "integration": "cloudtrail", "package": "aws", - "version": "1.10.2" + "version": "^1.5.0" } ], "required_fields": [ @@ -81,5 +81,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 101 + "version": 102 } From 8ab92b206a470ab995bde4649af97341729f50f4 Mon Sep 17 00:00:00 2001 From: christineweng <18648970+christineweng@users.noreply.github.com> Date: Thu, 29 Sep 2022 15:10:45 -0500 Subject: [PATCH 139/185] [Security Solution][Analyzer] Fixed alignment issues for process cube in analyzer (#141706) * fixed alignment issues for process cube in analyzer * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/resolver/view/panels/cube_for_process.tsx | 1 + .../public/resolver/view/panels/node_detail.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx index d16e1bec3c36..ef22f0f60edc 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx @@ -51,6 +51,7 @@ export const CubeForProcess = memo(function ({ viewBox="0 0 34 34" data-test-subj={dataTestSubj} isOrigin={isOrigin} + style={{ verticalAlign: 'middle' }} > {i18n.translate('xpack.securitySolution.resolver.node_icon', { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx index 2c4181add11c..a3d3251e4100 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx @@ -35,7 +35,6 @@ import { PanelContentError } from './panel_content_error'; const StyledCubeForProcess = styled(CubeForProcess)` position: relative; - top: 0.75em; `; const nodeDetailError = i18n.translate('xpack.securitySolution.resolver.panel.nodeDetail.Error', { From 0cfaff4deb81195f15b3dfc0ad7abf02bc59f268 Mon Sep 17 00:00:00 2001 From: Luke Gmys Date: Thu, 29 Sep 2022 22:31:01 +0200 Subject: [PATCH 140/185] [TIP] Align Threat Intel plugin loading states with the designs (#142200) --- .../indicators_barchart_wrapper.test.tsx.snap | 170 +----------- .../indicators_barchart_wrapper.stories.tsx | 242 ++++++++++++------ .../indicators_barchart_wrapper.test.tsx | 88 +++++-- .../indicators_barchart_wrapper.tsx | 49 +++- .../indicators_table.stories.tsx | 51 +++- .../indicators_table.test.tsx | 31 ++- .../indicators_table/indicators_table.tsx | 75 ++++-- .../hooks/use_aggregated_indicators.test.tsx | 2 + .../hooks/use_aggregated_indicators.ts | 13 +- .../modules/indicators/indicators_page.tsx | 18 +- 10 files changed, 428 insertions(+), 311 deletions(-) diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap index bc2b71303138..f7ae645c3b1d 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap @@ -1,115 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should render barchart and field selector dropdown 1`] = ` -Object { - "asFragment": [Function], - "baseElement": -
    -
    -
    -

    - Trend -

    -
    -
    -
    -
    - -
    -
    - - threat.feed.name - -
    - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - , - "container":
    +exports[` when not loading or refetching should render barchart and field selector dropdown 1`] = ` + +
    @@ -212,57 +107,6 @@ Object { class="echContainer" />
    -
    , - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} +
    + `; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx index f08e8f3b2f0e..ed7be8b06b4a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx @@ -18,104 +18,108 @@ import { StoryProvidersComponent } from '../../../../common/mocks/story_provider import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; -import { Aggregation, AGGREGATION_NAME } from '../../services/fetch_aggregated_indicators'; +import { + Aggregation, + AGGREGATION_NAME, + ChartSeries, +} from '../../services/fetch_aggregated_indicators'; export default { component: IndicatorsBarChartWrapper, title: 'IndicatorsBarChartWrapper', }; -export const Default: Story = () => { - const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; +const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; + +const mockIndexPattern: DataView = { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], +} as DataView; - const mockIndexPattern: DataView = { - fields: [ +const validDate: string = '1 Jan 2022 00:00:00 GMT'; +const numberOfDays: number = 1; +const aggregation1: Aggregation = { + events: { + buckets: [ { - name: '@timestamp', - type: 'date', - } as DataViewField, + doc_count: 0, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, { - name: 'threat.feed.name', - type: 'string', - } as DataViewField, + doc_count: 10, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, ], - } as DataView; - - const validDate: string = '1 Jan 2022 00:00:00 GMT'; - const numberOfDays: number = 1; - const aggregation1: Aggregation = { - events: { - buckets: [ - { - doc_count: 0, - key: 1641016800000, - key_as_string: '1 Jan 2022 06:00:00 GMT', - }, - { - doc_count: 10, - key: 1641038400000, - key_as_string: '1 Jan 2022 12:00:00 GMT', - }, - ], - }, - doc_count: 0, - key: '[Filebeat] AbuseCH Malware', - }; - const aggregation2: Aggregation = { - events: { - buckets: [ - { - doc_count: 20, - key: 1641016800000, - key_as_string: '1 Jan 2022 06:00:00 GMT', - }, - { - doc_count: 8, - key: 1641038400000, - key_as_string: '1 Jan 2022 12:00:00 GMT', - }, - ], - }, - doc_count: 0, - key: '[Filebeat] AbuseCH MalwareBazaar', - }; + }, + doc_count: 0, + key: '[Filebeat] AbuseCH Malware', +}; +const aggregation2: Aggregation = { + events: { + buckets: [ + { + doc_count: 20, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 8, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH MalwareBazaar', +}; - const dataServiceMock = { - search: { - search: () => - of({ - rawResponse: { - aggregations: { - [AGGREGATION_NAME]: { - buckets: [aggregation1, aggregation2], - }, +const dataServiceMock = { + search: { + search: () => + of({ + rawResponse: { + aggregations: { + [AGGREGATION_NAME]: { + buckets: [aggregation1, aggregation2], }, }, - }), - }, - query: { - timefilter: { - timefilter: { - calculateBounds: () => ({ - min: moment(validDate), - max: moment(validDate).add(numberOfDays, 'days'), - }), }, + }), + }, + query: { + timefilter: { + timefilter: { + calculateBounds: () => ({ + min: moment(validDate), + max: moment(validDate).add(numberOfDays, 'days'), + }), }, - filterManager: { - getFilters: () => {}, - setFilters: () => {}, - getUpdates$: () => of(), - }, }, - } as unknown as DataPublicPluginStart; + filterManager: { + getFilters: () => {}, + setFilters: () => {}, + getUpdates$: () => of(), + }, + }, +} as unknown as DataPublicPluginStart; - const uiSettingsMock = { - get: () => {}, - } as unknown as IUiSettingsClient; +const uiSettingsMock = { + get: () => {}, +} as unknown as IUiSettingsClient; - const timelinesMock = mockKibanaTimelinesService; +const timelinesMock = mockKibanaTimelinesService; +export const Default: Story = () => { return ( = () => { ); }; + Default.decorators = [(story) => {story()}]; + +export const InitialLoad: Story = () => { + return ( + + + + ); +}; + +InitialLoad.decorators = [(story) => {story()}]; + +export const UpdatingData: Story = () => { + const mockIndicators: ChartSeries[] = [ + { + x: '1 Jan 2022 00:00:00 GMT', + y: 2, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 00:00:00 GMT', + y: 10, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 25, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 18:00:00 GMT', + y: 15, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + ]; + + return ( + + + + ); +}; + +UpdatingData.decorators = [(story) => {story()}]; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx index 48d084b0e832..ef968be02c22 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx @@ -10,9 +10,11 @@ import { render } from '@testing-library/react'; import { TimeRange } from '@kbn/es-query'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; +import { + CHART_UPDATE_PROGRESS_TEST_ID, + IndicatorsBarChartWrapper, +} from './indicators_barchart_wrapper'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; -import { useFilters } from '../../../query_bar/hooks/use_filters'; import moment from 'moment'; jest.mock('../../../query_bar/hooks/use_filters'); @@ -32,33 +34,67 @@ const mockIndexPattern: DataView = { const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; -const stub = () => {}; - describe('', () => { - beforeEach(() => { - (useFilters as jest.MockedFunction).mockReturnValue({ - filters: [], - filterQuery: { language: 'kuery', query: '' }, - filterManager: {} as any, - handleSavedQuery: stub, - handleSubmitQuery: stub, - handleSubmitTimeRange: stub, + describe('when not loading or refetching', () => { + it('should render barchart and field selector dropdown', () => { + const component = render( + + + + ); + + expect(component.asFragment()).toMatchSnapshot(); + }); + }); + + describe('when loading for the first time', () => { + it('should render progress indicator', () => { + const component = render( + + + + ); + + expect(component.queryByRole('progressbar')).toBeInTheDocument(); }); }); - it('should render barchart and field selector dropdown', () => { - const component = render( - - - - ); - expect(component).toMatchSnapshot(); + describe('when updating the data', () => { + it('should render progress indicator', () => { + const component = render( + + + + ); + + expect(component.queryByTestId(CHART_UPDATE_PROGRESS_TEST_ID)).toBeInTheDocument(); + }); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx index 31148685e370..aabfecde3024 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx @@ -6,7 +6,14 @@ */ import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiPanel, + EuiProgress, + EuiTitle, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { TimeRange } from '@kbn/es-query'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; @@ -18,6 +25,8 @@ import { ChartSeries } from '../../services/fetch_aggregated_indicators'; const DEFAULT_FIELD = RawIndicatorFieldId.Feed; +export const CHART_UPDATE_PROGRESS_TEST_ID = 'tiBarchartWrapper-updating'; + export interface IndicatorsBarChartWrapperProps { /** * From and to values received from the KQL bar and passed down to the hook to query data. @@ -35,6 +44,12 @@ export interface IndicatorsBarChartWrapperProps { field: string; onFieldChange: (value: string) => void; + + /** Is initial load in progress? */ + isLoading?: boolean; + + /** Is data update in progress? */ + isFetching?: boolean; } /** @@ -42,9 +57,21 @@ export interface IndicatorsBarChartWrapperProps { * and handles retrieving aggregated indicator data. */ export const IndicatorsBarChartWrapper = memo( - ({ timeRange, indexPattern, series, dateRange, field, onFieldChange }) => { + ({ timeRange, indexPattern, isLoading, isFetching, series, dateRange, field, onFieldChange }) => { + if (isLoading) { + return ( + + + + + + + + ); + } + return ( - <> +
    @@ -64,12 +91,20 @@ export const IndicatorsBarChartWrapper = memo( /> - {timeRange ? ( + + {isFetching && ( + + )} + + {timeRange && ( - ) : ( - <> )} - +
    ); } ); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx index 6505996a26a7..95217171cb9e 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.stories.tsx @@ -36,7 +36,7 @@ const columnSettings = { onSort: stub, }, }; -export function WithIndicators() { +export function IndicatorsFullyLoaded() { const indicatorsFixture: Indicator[] = Array(10).fill(generateMockIndicator()); return ( @@ -62,6 +62,55 @@ export function WithIndicators() { ); } +export function FirstLoad() { + return ( + + + + ); +} + +export function DataUpdateInProgress() { + const indicatorsFixture: Indicator[] = Array(10).fill(generateMockIndicator()); + + return ( + + + + + + ); +} + export function WithNoIndicators() { return ( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx index 027033ae4771..eb1d4c3411c1 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.test.tsx @@ -7,7 +7,11 @@ import { act, render, screen } from '@testing-library/react'; import React from 'react'; -import { IndicatorsTable, IndicatorsTableProps } from './indicators_table'; +import { + IndicatorsTable, + IndicatorsTableProps, + TABLE_UPDATE_PROGRESS_TEST_ID, +} from './indicators_table'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; import { generateMockIndicator, Indicator } from '../../../../../common/types/indicator'; import { BUTTON_TEST_ID } from '../open_indicator_flyout_button'; @@ -56,7 +60,7 @@ const indicatorsFixture: Indicator[] = [ ]; describe('', () => { - it('should render loading spinner when loading', async () => { + it('should render loading spinner when doing initial loading', async () => { await act(async () => { render( @@ -68,6 +72,25 @@ describe('', () => { expect(screen.queryByRole('progressbar')).toBeInTheDocument(); }); + it('should render loading indicator when doing data update', async () => { + await act(async () => { + render( + + + + ); + }); + + screen.debug(); + + expect(screen.queryByTestId(TABLE_UPDATE_PROGRESS_TEST_ID)).toBeInTheDocument(); + }); + it('should render datagrid when loading is done', async () => { await act(async () => { render( @@ -75,6 +98,7 @@ describe('', () => { @@ -92,5 +116,8 @@ describe('', () => { }); expect(screen.queryByTestId(TITLE_TEST_ID)).toBeInTheDocument(); + + expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); + expect(screen.queryByTestId(TABLE_UPDATE_PROGRESS_TEST_ID)).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx index d1888431f7d8..739100347187 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_table/indicators_table.tsx @@ -13,6 +13,8 @@ import { EuiFlexItem, EuiLoadingSpinner, EuiPanel, + EuiProgress, + EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -39,7 +41,8 @@ export interface IndicatorsTableProps { /** * If true, no data is available yet */ - isLoading: boolean; + isLoading?: boolean; + isFetching?: boolean; indexPattern: SecuritySolutionDataViewBase; browserFields: BrowserFields; columnSettings: ColumnSettingsValue; @@ -54,6 +57,8 @@ const gridStyle = { fontSize: 's', } as const; +export const TABLE_UPDATE_PROGRESS_TEST_ID = `${TABLE_TEST_ID}-updating` as const; + export const IndicatorsTable: VFC = ({ indicators, indicatorCount, @@ -61,6 +66,7 @@ export const IndicatorsTable: VFC = ({ onChangeItemsPerPage, pagination, isLoading, + isFetching, browserFields, columnSettings: { columns, columnVisibility, handleResetColumns, handleToggleColumn, sorting }, }) => { @@ -157,44 +163,57 @@ export const IndicatorsTable: VFC = ({ } return ( - + <> + {isFetching && ( + + )} + + + + ); }, [ - columnVisibility, - mappedColumns, + isLoading, indicatorCount, + isFetching, leadingControlColumns, - isLoading, + renderCellValue, + toolbarOptions, + pagination, onChangeItemsPerPage, onChangePage, - pagination, - renderCellValue, sorting, - toolbarOptions, + columnVisibility, + mappedColumns, ]); return ( -
    - + +
    {flyoutFragment} {gridFragment} - -
    +
    + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx index 85c703cf5dca..21bff01fab76 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx @@ -88,6 +88,8 @@ describe('useAggregatedIndicators()', () => { "max": "2022-01-02T00:00:00.000Z", "min": "2022-01-01T00:00:00.000Z", }, + "isFetching": true, + "isLoading": true, "onFieldChange": [Function], "selectedField": "threat.feed.name", "series": Array [], diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts index 98e672ac3ad9..06b99202288b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts @@ -49,6 +49,12 @@ export interface UseAggregatedIndicatorsValue { * Indicator field used to query the aggregated Indicators. */ selectedField: string; + + /** Is initial load in progress? */ + isLoading?: boolean; + + /** Is data update in progress? */ + isFetching?: boolean; } const DEFAULT_FIELD = RawIndicatorFieldId.Feed; @@ -80,7 +86,7 @@ export const useAggregatedIndicators = ({ [inspectorAdapters, queryService, searchService] ); - const { data } = useQuery( + const { data, isLoading, isFetching } = useQuery( [ 'indicatorsBarchart', { @@ -97,7 +103,8 @@ export const useAggregatedIndicators = ({ }: { signal?: AbortSignal; queryKey: [string, FetchAggregatedIndicatorsParams]; - }) => aggregatedIndicatorsQuery(queryParams, signal) + }) => aggregatedIndicatorsQuery(queryParams, signal), + { keepPreviousData: true } ); const dateRange = useMemo( @@ -110,5 +117,7 @@ export const useAggregatedIndicators = ({ series: data || [], onFieldChange: setField, selectedField: field, + isLoading, + isFetching, }; }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx index fff2caad5715..5ad041300338 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx @@ -53,10 +53,11 @@ const IndicatorsPageContent: VFC = () => { handleRefresh, indicatorCount, indicators, - isLoading, onChangeItemsPerPage, onChangePage, pagination, + isLoading: isLoadingIndicators, + isFetching: isFetchingIndicators, } = useIndicators({ filters, filterQuery, @@ -64,7 +65,14 @@ const IndicatorsPageContent: VFC = () => { sorting: columnSettings.sorting.columns, }); - const { dateRange, series, selectedField, onFieldChange } = useAggregatedIndicators({ + const { + dateRange, + series, + selectedField, + onFieldChange, + isLoading: isLoadingAggregatedIndicators, + isFetching: isFetchingAggregatedIndicators, + } = useAggregatedIndicators({ timeRange, filters, filterQuery, @@ -97,7 +105,10 @@ const IndicatorsPageContent: VFC = () => { indexPattern={indexPattern} field={selectedField} onFieldChange={onFieldChange} + isFetching={isFetchingAggregatedIndicators} + isLoading={isLoadingAggregatedIndicators} /> + { pagination={pagination} indicatorCount={indicatorCount} indicators={indicators} - isLoading={isLoading} + isLoading={isLoadingIndicators} + isFetching={isFetchingIndicators} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={onChangePage} /> From 7274c272185ed87408ef2d32f11e6df5f5b2cf8a Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 29 Sep 2022 16:18:19 -0500 Subject: [PATCH 141/185] [ci/build] Add label for building example plugins (#139720) * [ci/build] Add label for building example plugins * update snapshots * remove plugins before tests * fix * fix again * fix formatting --- .buildkite/scripts/build_kibana.sh | 1 + .buildkite/scripts/download_build_artifacts.sh | 7 +++++++ .buildkite/scripts/steps/demo_env/Dockerfile | 2 -- .gitignore | 1 + src/dev/build/args.test.ts | 14 +++++++------- src/dev/build/args.ts | 2 +- src/dev/build/build_distributables.ts | 12 ++++-------- .../build/tasks/build_kibana_example_plugins.ts | 15 +++++++-------- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 90f9da8ac8de..2757c956920f 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -10,6 +10,7 @@ echo "--- Build Kibana Distribution" BUILD_ARGS="" is_pr_with_label "ci:build-all-platforms" && BUILD_ARGS="--all-platforms" +is_pr_with_label "ci:build-example-plugins" && BUILD_ARGS="$BUILD_ARGS --example-plugins" is_pr_with_label "ci:build-docker-cross-compile" && BUILD_ARG="$BUILD_ARGS --docker-cross-compile" is_pr_with_label "ci:build-os-packages" || BUILD_ARGS="$BUILD_ARGS --skip-os-packages" is_pr_with_label "ci:build-canvas-shareable-runtime" || BUILD_ARGS="$BUILD_ARGS --skip-canvas-shareable-runtime" diff --git a/.buildkite/scripts/download_build_artifacts.sh b/.buildkite/scripts/download_build_artifacts.sh index dd0ae660543a..1e793346da33 100755 --- a/.buildkite/scripts/download_build_artifacts.sh +++ b/.buildkite/scripts/download_build_artifacts.sh @@ -15,6 +15,13 @@ if [[ ! -d "$KIBANA_BUILD_LOCATION/bin" ]]; then mkdir -p "$KIBANA_BUILD_LOCATION" tar -xzf kibana-default.tar.gz -C "$KIBANA_BUILD_LOCATION" --strip=1 + if is_pr_with_label "ci:build-example-plugins"; then + # Testing against an example plugin distribution is not supported, + # mostly due to snapshot failures when testing UI element lists + rm -rf "$KIBANA_BUILD_LOCATION/plugins" + mkdir "$KIBANA_BUILD_LOCATION/plugins" + fi + cd "$KIBANA_DIR" tar -xzf ../kibana-default-plugins.tar.gz diff --git a/.buildkite/scripts/steps/demo_env/Dockerfile b/.buildkite/scripts/steps/demo_env/Dockerfile index a0b1c3311dc8..4e841c04bbf4 100644 --- a/.buildkite/scripts/steps/demo_env/Dockerfile +++ b/.buildkite/scripts/steps/demo_env/Dockerfile @@ -1,4 +1,2 @@ ARG BASE_IMAGE FROM ${BASE_IMAGE} -COPY ./* /var/lib/example_plugins -RUN find /var/lib/example_plugins/ -type f -name '*.zip' | xargs -I % /usr/share/kibana/bin/kibana-plugin install 'file://%' diff --git a/.gitignore b/.gitignore index 98b294dbd6dc..82a13e661a5b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ __tmp__ # Ignore example plugin builds /examples/*/build +/x-pack/examples/*/build # Ignore certain functional test runner artifacts /test/*/failure_debug diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index 6a7436284b2e..3e1b1c72f88f 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -28,13 +28,13 @@ it('build default and oss dist for current platform, without packages, by defaul Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, "createDockerUbuntu": false, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": false, @@ -62,13 +62,13 @@ it('builds packages if --all-platforms is passed', () => { Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, "createDockerUbuntu": true, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": true, @@ -96,13 +96,13 @@ it('limits packages if --rpm passed with --all-platforms', () => { Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, "createDockerUbuntu": false, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": true, @@ -130,13 +130,13 @@ it('limits packages if --deb passed with --all-platforms', () => { Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": true, "createDockerCloud": false, "createDockerContexts": true, "createDockerUBI": false, "createDockerUbuntu": false, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": false, @@ -165,13 +165,13 @@ it('limits packages if --docker passed with --all-platforms', () => { Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, "createDockerUbuntu": true, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": false, @@ -207,13 +207,13 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": false, "createDockerUbuntu": true, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": false, @@ -242,13 +242,13 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => Object { "buildOptions": Object { "buildCanvasShareableRuntime": true, + "buildExamplePlugins": false, "createArchives": true, "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, "createDockerUBI": true, "createDockerUbuntu": false, - "createExamplePlugins": false, "createGenericFolders": true, "createPlatformFolders": true, "createRpmPackage": true, diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index c3d1a19f82a3..c3cd4f164a48 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -127,7 +127,7 @@ export function readCliArgs(argv: string[]) { createGenericFolders: !Boolean(flags['skip-generic-folders']), createPlatformFolders: !Boolean(flags['skip-platform-folders']), createArchives: !Boolean(flags['skip-archives']), - createExamplePlugins: Boolean(flags['example-plugins']), + buildExamplePlugins: Boolean(flags['example-plugins']), createRpmPackage: isOsPackageDesired('rpm'), createDebPackage: isOsPackageDesired('deb'), createDockerUbuntu: diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index 0649c5ddc946..40a1afa51add 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -32,7 +32,7 @@ export interface BuildOptions { createDockerContexts: boolean; versionQualifier: string | undefined; targetAllPlatforms: boolean; - createExamplePlugins: boolean; + buildExamplePlugins: boolean; eprRegistry: 'production' | 'snapshot'; } @@ -58,13 +58,6 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.ExtractNodeBuilds); } - /** - * build example plugins - */ - if (options.createExamplePlugins) { - await run(Tasks.BuildKibanaExamplePlugins); - } - /** * run platform-generic build tasks */ @@ -79,6 +72,9 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.BuildCanvasShareableRuntime); } await run(Tasks.BuildKibanaPlatformPlugins); + if (options.buildExamplePlugins) { + await run(Tasks.BuildKibanaExamplePlugins); + } await run(Tasks.CreatePackageJson); await run(Tasks.InstallDependencies); await run(Tasks.GeneratePackagesOptimizedAssets); diff --git a/src/dev/build/tasks/build_kibana_example_plugins.ts b/src/dev/build/tasks/build_kibana_example_plugins.ts index 0208ba2ed61b..6fc53e10390c 100644 --- a/src/dev/build/tasks/build_kibana_example_plugins.ts +++ b/src/dev/build/tasks/build_kibana_example_plugins.ts @@ -9,14 +9,16 @@ import Path from 'path'; import Fs from 'fs'; import { REPO_ROOT } from '@kbn/utils'; -import { exec, mkdirp, copyAll, Task } from '../lib'; +import { exec, Task } from '../lib'; export const BuildKibanaExamplePlugins: Task = { description: 'Building distributable versions of Kibana example plugins', - async run(config, log) { + async run(config, log, build) { + const pluginsDir = build.resolvePath('plugins'); const args = [ Path.resolve(REPO_ROOT, 'scripts/plugin_helpers'), 'build', + '--skip-archive', `--kibana-version=${config.getBuildVersion()}`, ]; @@ -42,15 +44,12 @@ export const BuildKibanaExamplePlugins: Task = { cwd: examplePlugin, level: 'info', }); + log.info('Copying build to distribution'); + const pluginBuild = Path.resolve(examplePlugin, 'build', 'kibana'); + Fs.cpSync(pluginBuild, pluginsDir, { recursive: true }); } catch (e) { log.info(`Skipping ${examplePlugin}, no kibana.json`); } } - - const pluginsDir = config.resolveFromTarget('example_plugins'); - await mkdirp(pluginsDir); - await copyAll(REPO_ROOT, pluginsDir, { - select: ['examples/*/build/*.zip', 'x-pack/examples/*/build/*.zip'], - }); }, }; From 733d7e38ed8e5e340ce33331c9e4de79f829dc7f Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Thu, 29 Sep 2022 19:42:53 -0700 Subject: [PATCH 142/185] [RAM] Fix flaky rule details E2E tests (#138215) * Fix flaky rule details E2E tests * Clean up test entities * Add snooze while disabled test * Fix broken test due to tab changing order Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/triggers_actions_ui/details.ts | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 56dfa17ef626..d32c5bd58a94 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -136,9 +136,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return response; } - // Failing: See https://github.com/elastic/kibana/issues/129337 - // Failing: See https://github.com/elastic/kibana/issues/129337 - describe.skip('Rule Details', function () { + describe('Rule Details', function () { describe('Header', function () { const testRunUuid = uuid.v4(); before(async () => { @@ -200,19 +198,27 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - it('shouldnt allow you to snooze a disabled rule', async () => { + it('should allow you to snooze a disabled rule', async () => { const actionsDropdown = await testSubjects.find('statusDropdown'); expect(await actionsDropdown.getVisibleText()).to.eql('Disabled'); - await actionsDropdown.click(); - const actionsMenuElem = await testSubjects.find('ruleStatusMenu'); - const actionsMenuItemElem = await actionsMenuElem.findAllByClassName('euiContextMenuItem'); + let snoozeBadge = await testSubjects.find('rulesListNotifyBadge-unsnoozed'); + await snoozeBadge.click(); - expect(await actionsMenuItemElem.at(2)?.getVisibleText()).to.eql('Snooze'); - expect(await actionsMenuItemElem.at(2)?.getAttribute('disabled')).to.eql('true'); - // close the dropdown - await actionsDropdown.click(); + const snoozeIndefinite = await testSubjects.find('ruleSnoozeIndefiniteApply'); + await snoozeIndefinite.click(); + + await retry.try(async () => { + await testSubjects.existOrFail('rulesListNotifyBadge-snoozedIndefinitely'); + }); + + // Unsnooze the rule for the next test + snoozeBadge = await testSubjects.find('rulesListNotifyBadge-snoozedIndefinitely'); + await snoozeBadge.click(); + + const snoozeCancel = await testSubjects.find('ruleSnoozeCancel'); + await snoozeCancel.click(); }); it('should reenable a disabled the rule', async () => { @@ -232,42 +238,26 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should snooze the rule', async () => { - const actionsDropdown = await testSubjects.find('statusDropdown'); - - expect(await actionsDropdown.getVisibleText()).to.eql('Enabled'); - - await actionsDropdown.click(); - const actionsMenuElem = await testSubjects.find('ruleStatusMenu'); - const actionsMenuItemElem = await actionsMenuElem.findAllByClassName('euiContextMenuItem'); - - await actionsMenuItemElem.at(2)?.click(); + const snoozeBadge = await testSubjects.find('rulesListNotifyBadge-unsnoozed'); + await snoozeBadge.click(); const snoozeIndefinite = await testSubjects.find('ruleSnoozeIndefiniteApply'); await snoozeIndefinite.click(); await retry.try(async () => { - expect(await actionsDropdown.getVisibleText()).to.eql('Snoozed'); - const remainingSnoozeTime = await testSubjects.find('remainingSnoozeTime'); - expect(await remainingSnoozeTime.getVisibleText()).to.eql('Indefinitely'); + await testSubjects.existOrFail('rulesListNotifyBadge-snoozedIndefinitely'); }); }); it('should unsnooze the rule', async () => { - const actionsDropdown = await testSubjects.find('statusDropdown'); - - expect(await actionsDropdown.getVisibleText()).to.eql('Snoozed'); - - await actionsDropdown.click(); - const actionsMenuElem = await testSubjects.find('ruleStatusMenu'); - const actionsMenuItemElem = await actionsMenuElem.findAllByClassName('euiContextMenuItem'); - - await actionsMenuItemElem.at(2)?.click(); + const snoozeBadge = await testSubjects.find('rulesListNotifyBadge-snoozedIndefinitely'); + await snoozeBadge.click(); const snoozeCancel = await testSubjects.find('ruleSnoozeCancel'); await snoozeCancel.click(); await retry.try(async () => { - expect(await actionsDropdown.getVisibleText()).to.eql('Enabled'); + await testSubjects.existOrFail('rulesListNotifyBadge-unsnoozed'); }); }); }); @@ -510,6 +500,22 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(await testSubjects.exists('addNewActionConnectorActionGroup-0')).to.eql(true); expect(await testSubjects.exists('addNewActionConnectorActionGroup-1')).to.eql(true); + + // delete connector + await pageObjects.common.navigateToApp('triggersActions'); + // refresh to see alert + await browser.refresh(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + // verify content + await testSubjects.existOrFail('rulesList'); + + await pageObjects.triggersActionsUI.changeTabs('connectorsTab'); + await pageObjects.triggersActionsUI.searchConnectors('new connector'); + await testSubjects.click('deleteConnector'); + await testSubjects.existOrFail('deleteIdsConfirmation'); + await testSubjects.click('deleteIdsConfirmation > confirmModalConfirmButton'); + await testSubjects.missingOrFail('deleteIdsConfirmation'); }); }); @@ -890,6 +896,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the event log list and can filter/sort', async () => { await browser.refresh(); + await (await testSubjects.find('eventLogListTab')).click(); // Check to see if the experimental is enabled, if not, just return const tabbedContentExists = await testSubjects.exists('ruleDetailsTabbedContent'); From 66f209f4b20885a6749760eb04931e8428c36160 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:43:31 -0600 Subject: [PATCH 143/185] [api-docs] Daily api_docs build (#142298) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/custom_integrations.devdocs.json | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.devdocs.json | 42 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/fleet.devdocs.json | 16 + api_docs/fleet.mdx | 4 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- .../kbn_core_injected_metadata_browser.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.devdocs.json | 22 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 8 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.devdocs.json | 487 +++++++++++++++++- api_docs/visualizations.mdx | 4 +- 409 files changed, 961 insertions(+), 426 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 929e069b2bf5..27394a9d1337 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: 2022-09-29 +date: 2022-09-30 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 43856104f328..50e964f1d4fc 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 7694fbde6190..97c9deaf7e16 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: 2022-09-29 +date: 2022-09-30 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 fd1ed97c10f2..1d4419372cb8 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: 2022-09-29 +date: 2022-09-30 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 934ffbbd3380..894f1a412400 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 12a25911e2da..a88ae007a49f 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: 2022-09-29 +date: 2022-09-30 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 c4055d656405..9aa4dc3dc6cb 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: 2022-09-29 +date: 2022-09-30 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 4ef1b1b03a0e..98d04fe776c1 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: 2022-09-29 +date: 2022-09-30 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 6df29978541a..8e0ec02de952 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: 2022-09-29 +date: 2022-09-30 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 718e93231dff..0daa29277e4c 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: 2022-09-29 +date: 2022-09-30 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 74692e016ccf..8794f842b346 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 9b5efb2aa51f..67f93229e131 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 1ccfb42f61d0..e1a21c6f2cc8 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: 2022-09-29 +date: 2022-09-30 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 00e0779286aa..8ba5fe4e3728 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index e1427ab3d19f..b0885214467d 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 798c6c53ce34..38ceacd3fae0 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.devdocs.json b/api_docs/custom_integrations.devdocs.json index 03090384a055..44611a9497df 100644 --- a/api_docs/custom_integrations.devdocs.json +++ b/api_docs/custom_integrations.devdocs.json @@ -298,7 +298,7 @@ "label": "languageClientsUiComponents", "description": [], "signature": [ - "Map>" + "{ [x: string]: React.FC<{}>; }" ], "path": "src/plugins/custom_integrations/public/types.ts", "deprecated": false, diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 77c928f34e58..99cc96819059 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: 2022-09-29 +date: 2022-09-30 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 7a36e763bfea..f429f1771909 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: 2022-09-29 +date: 2022-09-30 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 06821933db67..1b406c8cf22e 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: 2022-09-29 +date: 2022-09-30 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 e5c75be5fa09..6b45be67575c 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index fdd7cb4298da..9c7512fae826 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: 2022-09-29 +date: 2022-09-30 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 619cec4220ea..6131baf37850 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index f0d1bf1f9d45..eb804887cbbb 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: 2022-09-29 +date: 2022-09-30 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 9800b25ff8ba..0360e887a0f1 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: 2022-09-29 +date: 2022-09-30 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 d2f23607c286..cf2021543491 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: 2022-09-29 +date: 2022-09-30 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 3c9d9dc38bae..9e9e13d1c714 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: 2022-09-29 +date: 2022-09-30 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 86bbfd4e83cf..a31d748cb362 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 547c12c8570c..32c98155e1b6 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 3943c847158f..52ccce27de73 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index da0e278bffa6..28ee76e3644f 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index b724ab15e695..9753d6519463 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: 2022-09-29 +date: 2022-09-30 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 39b2814d4136..d99574811ed8 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: 2022-09-29 +date: 2022-09-30 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 e9974fbb2bf4..46669e83965b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 8bbe0a063796..19cbeac71650 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: 2022-09-29 +date: 2022-09-30 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 c53b6a4aba7e..cad3566677f9 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: 2022-09-29 +date: 2022-09-30 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 6cc990a42eba..0964ad2d3db4 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: 2022-09-29 +date: 2022-09-30 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 8f80e15d3886..25628d88904b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 6a4ca6fcb773..31bbfd3cfa77 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 159a2f7167b0..70931ba483ca 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index b9be569d3a3e..57cb17ed7f57 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 6d9a8d244589..87f06ab7f0fc 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: 2022-09-29 +date: 2022-09-30 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 b38b786ca55b..e2d8790bf2b7 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: 2022-09-29 +date: 2022-09-30 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 9d856b882a31..03060b8617a5 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: 2022-09-29 +date: 2022-09-30 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 84225e80ad00..7ccb3fec0ef4 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: 2022-09-29 +date: 2022-09-30 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 e6902356f091..ea8581c29a05 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: 2022-09-29 +date: 2022-09-30 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 60a4d843aad3..b7d3b7b43451 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: 2022-09-29 +date: 2022-09-30 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 85c6421d292d..901d559b5a7e 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: 2022-09-29 +date: 2022-09-30 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 4d4a23fe3a6f..4f53bb28008e 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: 2022-09-29 +date: 2022-09-30 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 ff4af965c963..aeff45f40a77 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: 2022-09-29 +date: 2022-09-30 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 2d3ede34cf14..096441865046 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: 2022-09-29 +date: 2022-09-30 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 114bb0846272..237430ae0fb8 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: 2022-09-29 +date: 2022-09-30 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 6a87768ff01e..2316d7e48cc9 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index bf0c8f9c49ae..6c70b3fd09b1 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.devdocs.json b/api_docs/expressions.devdocs.json index bae5e63702f4..dfc68e39dd9c 100644 --- a/api_docs/expressions.devdocs.json +++ b/api_docs/expressions.devdocs.json @@ -363,7 +363,7 @@ "label": "invokeChain", "description": [], "signature": [ - "(chainArr: ", + "([head, ...tail]: ", { "pluginId": "expressions", "scope": "common", @@ -373,7 +373,15 @@ }, "[], input: unknown) => ", "Observable", - "" + "<", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueError", + "text": "ExpressionValueError" + }, + " | ChainOutput>" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -384,7 +392,7 @@ "id": "def-public.Execution.invokeChain.$1", "type": "Array", "tags": [], - "label": "chainArr", + "label": "[head, ...tail]", "description": [], "signature": [ { @@ -13369,7 +13377,7 @@ "label": "invokeChain", "description": [], "signature": [ - "(chainArr: ", + "([head, ...tail]: ", { "pluginId": "expressions", "scope": "common", @@ -13379,7 +13387,15 @@ }, "[], input: unknown) => ", "Observable", - "" + "<", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueError", + "text": "ExpressionValueError" + }, + " | ChainOutput>" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -13390,7 +13406,7 @@ "id": "def-server.Execution.invokeChain.$1", "type": "Array", "tags": [], - "label": "chainArr", + "label": "[head, ...tail]", "description": [], "signature": [ { @@ -22158,7 +22174,7 @@ "label": "invokeChain", "description": [], "signature": [ - "(chainArr: ", + "([head, ...tail]: ", { "pluginId": "expressions", "scope": "common", @@ -22168,7 +22184,15 @@ }, "[], input: unknown) => ", "Observable", - "" + "<", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueError", + "text": "ExpressionValueError" + }, + " | ChainOutput>" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -22179,7 +22203,7 @@ "id": "def-common.Execution.invokeChain.$1", "type": "Array", "tags": [], - "label": "chainArr", + "label": "[head, ...tail]", "description": [], "signature": [ { diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index cb5617b237f7..a2c88ec02316 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: 2022-09-29 +date: 2022-09-30 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 1c1d57aa91eb..821587d02923 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: 2022-09-29 +date: 2022-09-30 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 dc89d286ddfe..77ec5bd6a6db 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index f29f32174d12..71fd5ede2e0a 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: 2022-09-29 +date: 2022-09-30 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 3cc10c4a1612..5536609e6bf4 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 3059a8f9c9f8..59f0a3a2c3c9 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -9645,6 +9645,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "fleet", + "id": "def-common.FleetServerAgent.last_checkin_message", + "type": "string", + "tags": [], + "label": "last_checkin_message", + "description": [ + "\nLast checkin message" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/agent.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "fleet", "id": "def-common.FleetServerAgent.default_api_key_id", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 7c5beab0d5c9..61843d4ecb35 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 996 | 3 | 893 | 17 | +| 997 | 3 | 893 | 17 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 6803fa9d095e..8681c30dcf97 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: 2022-09-29 +date: 2022-09-30 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 062ffb1effb3..01249c9253db 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: 2022-09-29 +date: 2022-09-30 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 8f772016026c..21a00dd18b77 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 29de31f6cf68..08133b2e0ce4 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: 2022-09-29 +date: 2022-09-30 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 3dac89fa09d7..e6a58683dd57 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 315978011a0b..27d270ef0349 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 1eec7a67db10..69d8e811d233 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index ba3797cab67d..d425e08214e0 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index c6dcd6bde783..85eedf5bc8cb 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 78c88adbe04a..0fcb70c426aa 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 8e0003a6394a..7a5a7dc343e1 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index b936fa218da1..d3da6e6fc627 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 480d1389d1d5..b19adbf23a6b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 67d5f65e938a..cb3267bf94c0 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 2c75bd5664b6..6f92758ba29f 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index af39931531e6..42ba3bd8ac0d 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 5515fe767880..9e24183eab48 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index d85797746210..642cfc316df0 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index ec16bbb3798a..64f41b4de638 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: 2022-09-29 +date: 2022-09-30 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index cd86d11a33dc..a07ae6ec5b38 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 24e76f781315..ccabe37aa9fb 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index a38a76d26da3..904c2db7eeb9 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 96f35a5ca85a..3ef51f85a1dd 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: 2022-09-29 +date: 2022-09-30 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 efc5f97cca9f..4dbba9e0e4bb 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: 2022-09-29 +date: 2022-09-30 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 7030b63abaa1..a3510af5f70b 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: 2022-09-29 +date: 2022-09-30 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 464ef1f62934..c1bdc2321405 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: 2022-09-29 +date: 2022-09-30 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 7aac22dfeead..dd6763ce0a98 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index edd9cb125e75..b8a547e074cd 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: 2022-09-29 +date: 2022-09-30 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 b82dc689dba2..f2993b49d5f8 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: 2022-09-29 +date: 2022-09-30 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 cebca9163e79..62491682503d 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: 2022-09-29 +date: 2022-09-30 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 bd788d6e1868..cf14ce5a40d5 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index ac56135aa292..11c9a0928b58 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index eefeafb88d5d..76cb685c99f3 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: 2022-09-29 +date: 2022-09-30 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 36b99e421559..6c4d86bf0b72 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: 2022-09-29 +date: 2022-09-30 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 abf361ac547b..34614865e7e8 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: 2022-09-29 +date: 2022-09-30 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 ec45043e4726..1342382025c6 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: 2022-09-29 +date: 2022-09-30 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 04e49781988c..da2faf312d36 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: 2022-09-29 +date: 2022-09-30 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 126ab218962c..f2810dc16d62 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: 2022-09-29 +date: 2022-09-30 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 71e7fae504f5..b002114d3e33 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: 2022-09-29 +date: 2022-09-30 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 55beb31993bb..c726e5f8b787 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: 2022-09-29 +date: 2022-09-30 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 c777505da45d..fe234ab156ec 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: 2022-09-29 +date: 2022-09-30 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 10828fe0de71..f0450f42dbb9 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: 2022-09-29 +date: 2022-09-30 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 978ac4faed76..b486574b88c2 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: 2022-09-29 +date: 2022-09-30 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 16fd9445d124..6c09e87c4cc0 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: 2022-09-29 +date: 2022-09-30 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index cdaf427d6802..6f23a43cb533 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: 2022-09-29 +date: 2022-09-30 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 5bd2a717b5b8..98cc757ce42d 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: 2022-09-29 +date: 2022-09-30 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 76f931b4d756..69b473e30ee8 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: 2022-09-29 +date: 2022-09-30 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 77aa19a6800c..be0a688924da 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: 2022-09-29 +date: 2022-09-30 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 e6bfe1ecd7db..8eee81e91198 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: 2022-09-29 +date: 2022-09-30 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 e9475861b697..48ebe7f76b16 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: 2022-09-29 +date: 2022-09-30 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 d6dc431b07fd..6c6318b81245 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: 2022-09-29 +date: 2022-09-30 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 fdf7cc31d41f..0087fd5a9837 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index 73d538523658..cb7a24e95c61 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: 2022-09-29 +date: 2022-09-30 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 a1f512749d43..b98bb79392f8 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: 2022-09-29 +date: 2022-09-30 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 42574e3029ea..83a912dd03fd 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: 2022-09-29 +date: 2022-09-30 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 6bb0bb64c5ba..a3f274459a41 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: 2022-09-29 +date: 2022-09-30 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 938d8d6b38af..eeb3c991a0d6 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: 2022-09-29 +date: 2022-09-30 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 9d33c64bd7f4..2eb4bda78a5c 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index 7f254dda6eee..1edd0ca68c53 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 527a5f2be91e..35470535f96e 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: 2022-09-29 +date: 2022-09-30 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 7b470aeff355..b9f9b064de30 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: 2022-09-29 +date: 2022-09-30 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 34e0e5fb4d1f..118225780a1f 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: 2022-09-29 +date: 2022-09-30 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 97c4d2e6d4b4..c1331c1caec0 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: 2022-09-29 +date: 2022-09-30 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 0347b3f309a8..7c77af97bad8 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: 2022-09-29 +date: 2022-09-30 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 5cccdf33b503..3e100243e211 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: 2022-09-29 +date: 2022-09-30 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 e2c4d921a70b..83c04c073ea9 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: 2022-09-29 +date: 2022-09-30 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 1d786ddcd349..ea9ad18fb0ac 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: 2022-09-29 +date: 2022-09-30 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 25079df745e3..524e9382c019 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: 2022-09-29 +date: 2022-09-30 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 a5712d10acd3..2c5d8ce0deee 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: 2022-09-29 +date: 2022-09-30 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 2b2107d52082..dd5df0e31e51 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: 2022-09-29 +date: 2022-09-30 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 c9aa728e95fb..3e6df91ab7eb 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: 2022-09-29 +date: 2022-09-30 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 630ee26b5024..679fdb16b2af 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: 2022-09-29 +date: 2022-09-30 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 62ed3d4b8afa..bdb596d8b29e 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: 2022-09-29 +date: 2022-09-30 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 1c33ab738b0e..2832e5b25baa 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: 2022-09-29 +date: 2022-09-30 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 8d4402a7a2cd..765f996d9c46 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: 2022-09-29 +date: 2022-09-30 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 5e923792bc93..dd30432a2a83 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: 2022-09-29 +date: 2022-09-30 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 f2336d9fdfb0..f91bd759633c 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: 2022-09-29 +date: 2022-09-30 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 61e72d43c392..06e0a3a543a0 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: 2022-09-29 +date: 2022-09-30 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 1c31d922fc68..031735030635 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: 2022-09-29 +date: 2022-09-30 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 00772d47658c..5a354609a0a3 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: 2022-09-29 +date: 2022-09-30 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 7a8edbe3e3b3..d520c20c2d3b 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: 2022-09-29 +date: 2022-09-30 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 6dd6b8aebce6..bc6c34d1c07f 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: 2022-09-29 +date: 2022-09-30 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 8014dbb2ceb6..3faf57a4c881 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: 2022-09-29 +date: 2022-09-30 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 ed565e7b8f3b..83a7e913c082 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: 2022-09-29 +date: 2022-09-30 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 632f5546a721..10055c2130fd 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: 2022-09-29 +date: 2022-09-30 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 92184cbab278..acb60f56ebfc 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: 2022-09-29 +date: 2022-09-30 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 52f62aad5c93..9130f1f857a9 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: 2022-09-29 +date: 2022-09-30 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 0e41263673dd..4d1bfc14b6a6 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: 2022-09-29 +date: 2022-09-30 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 0fdb1bfe4318..d548463315b2 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index b758dea2dd26..b873c4a15d6e 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_http_server.mdx index bb3c7c1bd350..7e6e71fa6195 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 220d5282e1fc..8186ddd0f39a 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index ed120c45d977..fdeeded54cd6 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: 2022-09-29 +date: 2022-09-30 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 9cb11bc93a1f..cc8e5edcd399 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: 2022-09-29 +date: 2022-09-30 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 1bb71320adbc..2ca59b2bad48 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: 2022-09-29 +date: 2022-09-30 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 edbbec7b5aac..ba5a6502efe1 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: 2022-09-29 +date: 2022-09-30 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 408a8656056a..8847538b3a2d 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: 2022-09-29 +date: 2022-09-30 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 549c463617a5..e992ae37a3b7 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 11a4ba2c541e..39b237eedd45 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 579e9087903c..9e750bb7dba9 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: 2022-09-29 +date: 2022-09-30 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 c88718d0e930..ed82f8a5dea4 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: 2022-09-29 +date: 2022-09-30 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 6be7ee221009..f9f9e8a948c6 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: 2022-09-29 +date: 2022-09-30 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 863f69b31518..f9376e67d699 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: 2022-09-29 +date: 2022-09-30 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 add898c16abe..fa9f4053803d 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: 2022-09-29 +date: 2022-09-30 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index a488e40d01dd..7f27d332faca 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: 2022-09-29 +date: 2022-09-30 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 87082d1e3319..3d5bfb44851b 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: 2022-09-29 +date: 2022-09-30 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 7a746c627805..84ab6fff88ff 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: 2022-09-29 +date: 2022-09-30 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 a413d283cb4c..3e9e378809f2 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: 2022-09-29 +date: 2022-09-30 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 3ad9c040ab8f..6f502bf2c3bc 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: 2022-09-29 +date: 2022-09-30 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 4e9e0445e3f7..85f6d9268f7c 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: 2022-09-29 +date: 2022-09-30 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 0cfd64309de9..fcc2c9452311 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: 2022-09-29 +date: 2022-09-30 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 40f97497e1cf..5683a2d792ef 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: 2022-09-29 +date: 2022-09-30 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 3e4d25261b36..8b1732b474d9 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: 2022-09-29 +date: 2022-09-30 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 9ec6da0be76f..cb8ab729b92d 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: 2022-09-29 +date: 2022-09-30 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 e1be058f6694..983e300999f7 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: 2022-09-29 +date: 2022-09-30 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 2f8dd3d2d3cf..6832c1707041 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: 2022-09-29 +date: 2022-09-30 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 99c39b3ae3bf..b86953b92d74 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: 2022-09-29 +date: 2022-09-30 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 b1abfcae9d1d..d400da3e6594 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: 2022-09-29 +date: 2022-09-30 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 6af7ea7a401a..457f058a1c21 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: 2022-09-29 +date: 2022-09-30 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 4e53620f210c..03fa3b8be079 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: 2022-09-29 +date: 2022-09-30 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 3cdf63fa196b..187d1c690e2d 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: 2022-09-29 +date: 2022-09-30 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 ed9822ce6524..6dca74dc0384 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: 2022-09-29 +date: 2022-09-30 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 0e35863807e9..8ef53aa00edb 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: 2022-09-29 +date: 2022-09-30 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 e8513df53c90..f023cecb27a9 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: 2022-09-29 +date: 2022-09-30 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index a33965c6f024..89514146e62f 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: 2022-09-29 +date: 2022-09-30 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 499f42fd91b2..5f530e17cbf2 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: 2022-09-29 +date: 2022-09-30 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 cedfc5029275..3f6c17f74aba 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: 2022-09-29 +date: 2022-09-30 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index de5b9aa205ca..4d731485bd3b 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: 2022-09-29 +date: 2022-09-30 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 7628f073dc94..6297a248ab16 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: 2022-09-29 +date: 2022-09-30 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index a91351cf9f73..7784682515d4 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 fa562b3b2acf..9b590bd554e8 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: 2022-09-29 +date: 2022-09-30 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 acc5f724b1bc..321c7d8761b6 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: 2022-09-29 +date: 2022-09-30 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 114ad87c44b3..13287c8f8e30 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: 2022-09-29 +date: 2022-09-30 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 6731e2111977..bb9f4b645d23 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: 2022-09-29 +date: 2022-09-30 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 276569792284..676a2bb19365 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: 2022-09-29 +date: 2022-09-30 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 9f0c9ea82ba6..93c77175614e 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: 2022-09-29 +date: 2022-09-30 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 bc3ea822a942..3d8036576123 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: 2022-09-29 +date: 2022-09-30 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 b5f3e61df18b..d9f37e38d76b 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: 2022-09-29 +date: 2022-09-30 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 99e95bfdab28..77f3b314f46f 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: 2022-09-29 +date: 2022-09-30 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 5edf8cbfddef..1a389643784b 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: 2022-09-29 +date: 2022-09-30 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 5ed2a4976d1c..5f12f65364d0 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: 2022-09-29 +date: 2022-09-30 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 8f12d48e0f7a..945817f8b5ed 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: 2022-09-29 +date: 2022-09-30 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 6de1b336be40..128f95a96f7f 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: 2022-09-29 +date: 2022-09-30 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 4fb52fecf871..52533c9b6fe3 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: 2022-09-29 +date: 2022-09-30 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 71fdb228446e..e1795a3be133 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: 2022-09-29 +date: 2022-09-30 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 49314722d46a..d6ff451e1d1d 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: 2022-09-29 +date: 2022-09-30 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 266265f2ee05..e3a2233f32a1 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: 2022-09-29 +date: 2022-09-30 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 8d222376f5bf..2a073386f7a7 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: 2022-09-29 +date: 2022-09-30 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 f8cae5c4a550..da2f56a7f69c 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: 2022-09-29 +date: 2022-09-30 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 0dacdfd5af97..db4c4003ec79 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: 2022-09-29 +date: 2022-09-30 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 99b4036abb6b..c8b0a759e9da 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: 2022-09-29 +date: 2022-09-30 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 549cdfe3a58e..a46dd1b958ad 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: 2022-09-29 +date: 2022-09-30 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index c429de87e13d..bf4074f9ff80 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: 2022-09-29 +date: 2022-09-30 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 0888bc8875a2..50d9d7c37816 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 254373b7a14d..876f4bd186bb 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: 2022-09-29 +date: 2022-09-30 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 61ceae147d70..433f7324e549 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: 2022-09-29 +date: 2022-09-30 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 9b9365bc3081..0bf1ae7c2410 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: 2022-09-29 +date: 2022-09-30 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 baf48b87a0eb..577822a31bc1 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: 2022-09-29 +date: 2022-09-30 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 1aba4f31960a..30759d2ba604 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: 2022-09-29 +date: 2022-09-30 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 b2fe92199314..b00037c8b73c 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: 2022-09-29 +date: 2022-09-30 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 91c104b8b9de..0dbf6764d598 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: 2022-09-29 +date: 2022-09-30 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 31ba42e2554f..f2e11e0ec410 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index a25b0cfc501c..fdafd7c74c00 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 101431a9c73d..8e7cee968fe7 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: 2022-09-29 +date: 2022-09-30 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 710a3c62d544..bfcb2471b8f0 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: 2022-09-29 +date: 2022-09-30 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_crypto.mdx b/api_docs/kbn_crypto.mdx index a8c5ebefe66c..d47e0499bc2a 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: 2022-09-29 +date: 2022-09-30 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 b7a8b4187db8..471c3b25429d 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 77c10c205e81..4dc98f350e51 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index f79bd78824a0..0691a727d305 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: 2022-09-29 +date: 2022-09-30 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 156f2ad1e34d..7f92e41d7a4d 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: 2022-09-29 +date: 2022-09-30 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 c40a114fb200..720562339cb0 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: 2022-09-29 +date: 2022-09-30 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 6c817ebf71e7..9b0d11e055ba 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 055b95affc24..a972f8dfe08c 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: 2022-09-29 +date: 2022-09-30 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 f6a0a8c29e49..7fde88f253ff 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index fa63acb9d852..b60e5a143225 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 9f608a801f2c..5b8334a6273f 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: 2022-09-29 +date: 2022-09-30 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 1498ddb5d263..cbd8011fd264 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: 2022-09-29 +date: 2022-09-30 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 537db266a40f..6454ec64eeec 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: 2022-09-29 +date: 2022-09-30 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 923c644404d7..9d9912a3e60b 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: 2022-09-29 +date: 2022-09-30 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 23893848967a..460d6cc14eab 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index a29e11c7f3f5..de71112dafad 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index c3673495d2d9..4e028126633c 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: 2022-09-29 +date: 2022-09-30 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 29968a934531..04d7cb7b4cfd 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: 2022-09-29 +date: 2022-09-30 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_generate.mdx b/api_docs/kbn_generate.mdx index 535192fd2288..d88b4e834bee 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 270341543e97..11f578d5d6c5 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 75d249e64bf7..b3a8defe7720 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: 2022-09-29 +date: 2022-09-30 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 d7346ce1b677..e9429b41deb7 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index b1ce2043e27f..f678cb181de1 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: 2022-09-29 +date: 2022-09-30 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 63510d74b009..404f2163c46c 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: 2022-09-29 +date: 2022-09-30 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 ce7de735a110..525cb60f347a 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index a96adba4a09b..d4f49a570463 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index d35da6c7ae88..9fa8dfc8321c 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 623552a19ab9..4c08eeab4c88 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 46b40beb8bf3..d8948bee8640 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: 2022-09-29 +date: 2022-09-30 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 e9b4b290f7d1..e2a2cc005cad 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 3f19d6bc0c35..3999084bdf28 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index da43bfec51c4..6f279b0700ac 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: 2022-09-29 +date: 2022-09-30 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 2770d7ccb6ac..cc05defa4dac 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 83b9321c6a3f..20c5f25e026b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index ae4854e96ac2..a0dfd1545060 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index dd5f5c5ace18..8c10def45c78 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: 2022-09-29 +date: 2022-09-30 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 4a750b0b7dfa..f4eb153961bd 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: 2022-09-29 +date: 2022-09-30 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 170269826262..8d36ae013263 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 987ec45f387a..2a32d7d919b0 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index ed7566b575b5..ad7149dc8fe1 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: 2022-09-29 +date: 2022-09-30 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 7c45eeef92b5..8d83ba6d1f0e 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: 2022-09-29 +date: 2022-09-30 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 859b6a067644..9bd69c34940d 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: 2022-09-29 +date: 2022-09-30 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index aedd0101a45a..e87e2bea0731 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: 2022-09-29 +date: 2022-09-30 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index bfd4b4e6b5af..dff42547b8b4 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: 2022-09-29 +date: 2022-09-30 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 3ca9f9e155f1..9dfa323c764a 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 293fa5190545..233fdfa2b529 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index aaf5d45ea057..e7e506f42c4d 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 4f65db7503fc..2385663d0372 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 5a9078575773..c9560b881efc 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index dc5fcdbd1d0f..277e8fe2e29f 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: 2022-09-29 +date: 2022-09-30 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 1bcc5017dfc5..aada63b6cdcf 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: 2022-09-29 +date: 2022-09-30 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 a22707f2957b..06a05479ba72 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: 2022-09-29 +date: 2022-09-30 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 cdc1b3ea54ed..4ff62a3c86d9 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: 2022-09-29 +date: 2022-09-30 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 185f8a6c2a54..73431dba715a 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: 2022-09-29 +date: 2022-09-30 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 ee826f79d080..f1065703e694 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: 2022-09-29 +date: 2022-09-30 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 79363d1648a7..0a3e38566ee9 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: 2022-09-29 +date: 2022-09-30 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 bad03cd4ead9..0dc5e87dba56 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: 2022-09-29 +date: 2022-09-30 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 d2e797f19a3e..ee9a5057cd0b 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: 2022-09-29 +date: 2022-09-30 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 aadd62030a1b..75f29aaf7123 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: 2022-09-29 +date: 2022-09-30 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 37f5836577b4..515b4a8285bb 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: 2022-09-29 +date: 2022-09-30 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 7aa2954ee0c0..2991cdacbacd 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: 2022-09-29 +date: 2022-09-30 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 99fe2b585b0b..92b1867f34a6 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: 2022-09-29 +date: 2022-09-30 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 e132797fea85..a9ff8d1d81fb 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: 2022-09-29 +date: 2022-09-30 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 48d0d4756ef6..91cf5bb3b5dd 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: 2022-09-29 +date: 2022-09-30 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 5c184c9a583c..cefe3d1543a9 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 249f6a00cf7e..737c986cbf11 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: 2022-09-29 +date: 2022-09-30 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index e5e0f236974f..eebd51935c35 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index b78abda31ac5..c121d1e8a489 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index dda07aa08205..ff5390338683 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: 2022-09-29 +date: 2022-09-30 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 92fa45970a95..12b820666a65 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: 2022-09-29 +date: 2022-09-30 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 67fd1537b953..c083f9ea1266 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: 2022-09-29 +date: 2022-09-30 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index dba90c297e1b..35439d9fda00 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: 2022-09-29 +date: 2022-09-30 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index eb63fe71719d..de78cc05fd16 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: 2022-09-29 +date: 2022-09-30 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 312cc6dc442d..b1c6f91ebdbd 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: 2022-09-29 +date: 2022-09-30 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 fc5ddb138a55..9f07e742a760 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: 2022-09-29 +date: 2022-09-30 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 00f4e0b05251..065a90d00a34 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: 2022-09-29 +date: 2022-09-30 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 22e5de7cb344..c100281c25d4 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: 2022-09-29 +date: 2022-09-30 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 de0d8b5ff101..b884d72ca4d0 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: 2022-09-29 +date: 2022-09-30 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 b1597719e26a..c060047529bc 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: 2022-09-29 +date: 2022-09-30 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 254ce88f6ed0..362eb12bc58f 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: 2022-09-29 +date: 2022-09-30 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 4b7ce948af06..4e02c245ff69 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: 2022-09-29 +date: 2022-09-30 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 4bebd9fa27f9..49e658a38c8b 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: 2022-09-29 +date: 2022-09-30 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 a3b2ddd97a17..5e2e62553998 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: 2022-09-29 +date: 2022-09-30 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 0a1d772cc031..eec2b418bbc4 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: 2022-09-29 +date: 2022-09-30 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 70d3cf84c5ef..f6bf840ae41a 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: 2022-09-29 +date: 2022-09-30 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 61fe760de503..deb460019bbe 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: 2022-09-29 +date: 2022-09-30 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 02064c4eacc6..b6141feed65d 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: 2022-09-29 +date: 2022-09-30 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 86606778f8df..9d6b7561cd4f 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: 2022-09-29 +date: 2022-09-30 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 180ae33f7885..1d57383c1f7a 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: 2022-09-29 +date: 2022-09-30 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 7c2f7cc09c77..f1529dbdeead 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index ef8d669ed27a..08e81f53f5e6 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: 2022-09-29 +date: 2022-09-30 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 0e8f9505b35f..f9a40e4e5060 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index c2a9a2019695..0be3ac9e885d 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: 2022-09-29 +date: 2022-09-30 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 9662e0b7958f..12855b1d6c6e 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: 2022-09-29 +date: 2022-09-30 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 f279425a1128..ff504977fbd1 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 70dd586f3b9f..c4980baa0846 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: 2022-09-29 +date: 2022-09-30 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 2d4b1f7a3e30..fc2d3aa4ceb2 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index f0c472ec736d..c0fe27305880 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: 2022-09-29 +date: 2022-09-30 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 421399623af6..a11f331d327f 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 8c06707332c2..634503f23763 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index a03912522bea..ec79f6d74e50 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 7d6e18575e25..64a86ae7f718 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 3d387dbca9d5..fd83134954ef 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: 2022-09-29 +date: 2022-09-30 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index 80555e578ee9..e05e7d1fbf27 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 0396c24bc40e..c62021b90a7c 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: 2022-09-29 +date: 2022-09-30 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 de93a8fe050a..383e50529e74 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: 2022-09-29 +date: 2022-09-30 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 fe8d933d19a3..900ea8d6e615 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: 2022-09-29 +date: 2022-09-30 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 e9e1e8d327df..224a3cfeb7af 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 96ce8b5f78b1..51c6ae605e7b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 2a28c3743374..c15bfc3b7fe2 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: 2022-09-29 +date: 2022-09-30 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 7ab80f87e401..ea705da0da76 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: 2022-09-29 +date: 2022-09-30 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 21d349683d8e..0ea7624514ef 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: 2022-09-29 +date: 2022-09-30 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 b7d40f28432a..ac719690669b 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: 2022-09-29 +date: 2022-09-30 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 4d4561547ed2..43a256fa418c 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -4532,7 +4532,15 @@ }, " | ", "VisualizeEditorContext", - " | undefined) => T) | undefined" + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.Configuration", + "text": "Configuration" + }, + "> | undefined) => T) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -4586,7 +4594,15 @@ }, " | ", "VisualizeEditorContext", - " | undefined" + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.Configuration", + "text": "Configuration" + }, + "> | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -6415,7 +6431,7 @@ "signature": [ "((props: VisualizationStateFromContextChangeProps) => ", "Suggestion", - ") | undefined" + " | undefined) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 0cb5e40b3b4c..ebeef2fc8c31 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: 2022-09-29 +date: 2022-09-30 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 63d15a7c4065..7a642bff9e73 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: 2022-09-29 +date: 2022-09-30 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 88e17bcee6fe..0f646773d613 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: 2022-09-29 +date: 2022-09-30 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 c7000d51761c..13e56db522d2 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 9deb279fb73b..4a6747d66e72 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 2cec82774807..3b4067947d08 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: 2022-09-29 +date: 2022-09-30 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 2667e8f19bae..e68653a32c8c 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: 2022-09-29 +date: 2022-09-30 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 558352f89505..8547850fa777 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index b1abe52cefe1..b52f48b93500 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index b9332eb4163e..eaa35953f39e 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: 2022-09-29 +date: 2022-09-30 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 23c398899ccb..4048c3102a29 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index bfa907ab0926..4b05d9ce3b7e 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 4336085d004c..b10f893f9601 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index db3f0906e614..758f400939d9 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index c569e301708d..40c14dd0feeb 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 568f38705df0..faf5c1b11320 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 31953 | 179 | 21502 | 1003 | +| 31986 | 179 | 21534 | 1003 | ## Plugin Directory @@ -81,7 +81,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 5 | 249 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | 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. | 62 | 0 | 62 | 2 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 263 | 0 | 14 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 996 | 3 | 893 | 17 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 997 | 3 | 893 | 17 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -175,7 +175,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 53 | 0 | 50 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 693 | 12 | 663 | 18 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 725 | 12 | 695 | 18 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 099351a2d0f2..4d3613a8b117 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: 2022-09-29 +date: 2022-09-30 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 2ef17ce2192f..29db7898a4b5 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 8a248f78f1fa..49b546ef38a2 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: 2022-09-29 +date: 2022-09-30 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 9589edc812df..18eff91aea12 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: 2022-09-29 +date: 2022-09-30 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 1964dc994cd2..099cff0ab518 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: 2022-09-29 +date: 2022-09-30 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 d4539ef64efa..c2aaaeb78852 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: 2022-09-29 +date: 2022-09-30 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 8112f8684b55..5c28ac8b2bdb 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: 2022-09-29 +date: 2022-09-30 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 613f942ef906..6351e3d45b6e 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: 2022-09-29 +date: 2022-09-30 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 23cf7d31ca48..b0955d53c8d0 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: 2022-09-29 +date: 2022-09-30 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 c9cc4aa61ea7..ddb1f6d02750 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: 2022-09-29 +date: 2022-09-30 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 1450399aefd8..f9510b48f7ef 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: 2022-09-29 +date: 2022-09-30 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 1ed5aa9d511e..3239724d7ef3 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: 2022-09-29 +date: 2022-09-30 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 6c1900781f66..b11f668fa116 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: 2022-09-29 +date: 2022-09-30 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 883f8e26e2cc..42b62ddb1d6d 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: 2022-09-29 +date: 2022-09-30 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 df0feab2c445..2e8f47d4893b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 814d0173f69c..a576dc7f8b98 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: 2022-09-29 +date: 2022-09-30 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 a86c48a48dfc..575793a05728 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 66978ad664e0..2ccc09799e6f 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: 2022-09-29 +date: 2022-09-30 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 0086ad02ab5a..3c787d2f24b0 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 7bce31d6e183..fcd7d2325c17 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: 2022-09-29 +date: 2022-09-30 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 decf22f6da09..a0c0f7d6c4c2 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: 2022-09-29 +date: 2022-09-30 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 e68ecfcdfb99..6c9a14514a8a 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: 2022-09-29 +date: 2022-09-30 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 5363b913fbe0..fb2aab7cf00a 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: 2022-09-29 +date: 2022-09-30 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 7377ef34d529..5289e0a7dee5 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: 2022-09-29 +date: 2022-09-30 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 1a95f8384f24..ec6a6fda9343 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: 2022-09-29 +date: 2022-09-30 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 355a38255c9c..28813ee2279e 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: 2022-09-29 +date: 2022-09-30 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 e607fc3598bd..167b467328b2 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: 2022-09-29 +date: 2022-09-30 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 f538309dd462..c11efc4d4370 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: 2022-09-29 +date: 2022-09-30 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 bcebf8f8b934..8d9b53a137bc 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: 2022-09-29 +date: 2022-09-30 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 7a96d016a99c..5070df796c8a 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: 2022-09-29 +date: 2022-09-30 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 7d3c611c084b..3115cbb40769 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: 2022-09-29 +date: 2022-09-30 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 e40b4b0c4d44..c7570c524590 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: 2022-09-29 +date: 2022-09-30 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 9a4994646988..9626982ef723 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: 2022-09-29 +date: 2022-09-30 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 e1fd951d5e2a..5e08f17c4b21 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 254b1228a31f..2c376ec69d7e 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 5015d2debdc4..de104e703fd9 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: 2022-09-29 +date: 2022-09-30 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 5a40018e6081..6138b56184d2 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index f57d70892e70..e44699c7b7c5 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: 2022-09-29 +date: 2022-09-30 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 f40f15636c94..b59d6dcec268 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: 2022-09-29 +date: 2022-09-30 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 d462e4d19616..b5e23e55dfb6 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: 2022-09-29 +date: 2022-09-30 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 0b2993331260..2fb7351aa02f 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: 2022-09-29 +date: 2022-09-30 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 cc20cda9db10..408e2c2ca376 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: 2022-09-29 +date: 2022-09-30 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 609c06b1a92c..fa44a39b582b 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: 2022-09-29 +date: 2022-09-30 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 3f8c15b314a0..cd176ecf49a9 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: 2022-09-29 +date: 2022-09-30 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 5aa2bd8b01f5..d5b90e601d8e 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: 2022-09-29 +date: 2022-09-30 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 2219fc96f2e3..ba692d609d71 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: 2022-09-29 +date: 2022-09-30 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 2608ea12c24f..4b7469040671 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: 2022-09-29 +date: 2022-09-30 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 e535d8ca970e..9eddbfeec41d 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: 2022-09-29 +date: 2022-09-30 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 c6bd5fba0b06..31e138d68aa1 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: 2022-09-29 +date: 2022-09-30 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 8caa2a67dab8..a1d6cb915412 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: 2022-09-29 +date: 2022-09-30 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 95b3390b94ce..befca8a83486 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -9665,6 +9665,327 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState", + "type": "Interface", + "tags": [], + "label": "PartitionLayerState", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.layerId", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.layerType", + "type": "CompoundType", + "tags": [], + "label": "layerType", + "description": [], + "signature": [ + "\"data\" | \"referenceLine\" | \"annotations\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.primaryGroups", + "type": "Array", + "tags": [], + "label": "primaryGroups", + "description": [], + "signature": [ + "string[]" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.secondaryGroups", + "type": "Array", + "tags": [], + "label": "secondaryGroups", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.metric", + "type": "string", + "tags": [], + "label": "metric", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.collapseFns", + "type": "Object", + "tags": [], + "label": "collapseFns", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.numberDisplay", + "type": "CompoundType", + "tags": [], + "label": "numberDisplay", + "description": [], + "signature": [ + "\"value\" | \"percent\" | \"hidden\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.categoryDisplay", + "type": "CompoundType", + "tags": [], + "label": "categoryDisplay", + "description": [], + "signature": [ + "\"default\" | \"hide\" | \"inside\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.legendDisplay", + "type": "CompoundType", + "tags": [], + "label": "legendDisplay", + "description": [], + "signature": [ + "\"default\" | \"hide\" | \"show\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.legendPosition", + "type": "CompoundType", + "tags": [], + "label": "legendPosition", + "description": [], + "signature": [ + "Position", + " | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.showValuesInLegend", + "type": "CompoundType", + "tags": [], + "label": "showValuesInLegend", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.nestedLegend", + "type": "CompoundType", + "tags": [], + "label": "nestedLegend", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.percentDecimals", + "type": "number", + "tags": [], + "label": "percentDecimals", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.emptySizeRatio", + "type": "number", + "tags": [], + "label": "emptySizeRatio", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.legendMaxLines", + "type": "number", + "tags": [], + "label": "legendMaxLines", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.legendSize", + "type": "CompoundType", + "tags": [], + "label": "legendSize", + "description": [], + "signature": [ + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.LegendSize", + "text": "LegendSize" + }, + " | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionLayerState.truncateLegend", + "type": "CompoundType", + "tags": [], + "label": "truncateLegend", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionVisConfiguration", + "type": "Interface", + "tags": [], + "label": "PartitionVisConfiguration", + "description": [], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionVisConfiguration.shape", + "type": "CompoundType", + "tags": [], + "label": "shape", + "description": [], + "signature": [ + "\"pie\" | \"donut\" | \"treemap\" | \"mosaic\" | \"waffle\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionVisConfiguration.layers", + "type": "Array", + "tags": [], + "label": "layers", + "description": [], + "signature": [ + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.PartitionLayerState", + "text": "PartitionLayerState" + }, + "[]" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionVisConfiguration.palette", + "type": "Object", + "tags": [], + "label": "palette", + "description": [], + "signature": [ + "PaletteOutput", + "<{ [key: string]: unknown; }> | undefined" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.PercentileParams", @@ -12007,6 +12328,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.CategoryDisplayType", + "type": "Type", + "tags": [], + "label": "CategoryDisplayType", + "description": [], + "signature": [ + "\"default\" | \"hide\" | \"inside\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.Column", @@ -12090,6 +12426,14 @@ "text": "TableVisConfiguration" }, " | ", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.PartitionVisConfiguration", + "text": "PartitionVisConfiguration" + }, + " | ", { "pluginId": "visualizations", "scope": "common", @@ -12532,6 +12876,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.LayerType", + "type": "Type", + "tags": [], + "label": "LayerType", + "description": [], + "signature": [ + "\"data\" | \"referenceLine\" | \"annotations\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.LegendDisplayType", + "type": "Type", + "tags": [], + "label": "LegendDisplayType", + "description": [], + "signature": [ + "\"default\" | \"hide\" | \"show\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.MaxColumn", @@ -12715,6 +13089,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.NumberDisplayType", + "type": "Type", + "tags": [], + "label": "NumberDisplayType", + "description": [], + "signature": [ + "\"value\" | \"percent\" | \"hidden\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.Operation", @@ -12760,6 +13149,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionChartType", + "type": "Type", + "tags": [], + "label": "PartitionChartType", + "description": [], + "signature": [ + "\"pie\" | \"donut\" | \"treemap\" | \"mosaic\" | \"waffle\"" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.PercentileColumn", @@ -13210,6 +13614,21 @@ } ], "objects": [ + { + "parentPluginId": "visualizations", + "id": "def-common.CategoryDisplayTypes", + "type": "Object", + "tags": [], + "label": "CategoryDisplayTypes", + "description": [], + "signature": [ + "{ readonly DEFAULT: \"default\"; readonly INSIDE: \"inside\"; readonly HIDE: \"hide\"; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.FillTypes", @@ -13220,7 +13639,37 @@ "signature": [ "{ readonly NONE: \"none\"; readonly ABOVE: \"above\"; readonly BELOW: \"below\"; }" ], - "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.LayerTypes", + "type": "Object", + "tags": [], + "label": "LayerTypes", + "description": [], + "signature": [ + "{ readonly DATA: \"data\"; readonly REFERENCELINE: \"referenceLine\"; readonly ANNOTATIONS: \"annotations\"; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "visualizations", + "id": "def-common.LegendDisplayTypes", + "type": "Object", + "tags": [], + "label": "LegendDisplayTypes", + "description": [], + "signature": [ + "{ readonly DEFAULT: \"default\"; readonly SHOW: \"show\"; readonly HIDE: \"hide\"; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -13240,6 +13689,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.NumberDisplayTypes", + "type": "Object", + "tags": [], + "label": "NumberDisplayTypes", + "description": [], + "signature": [ + "{ readonly HIDDEN: \"hidden\"; readonly PERCENT: \"percent\"; readonly VALUE: \"value\"; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.Operations", @@ -13285,6 +13749,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-common.PartitionChartTypes", + "type": "Object", + "tags": [], + "label": "PartitionChartTypes", + "description": [], + "signature": [ + "{ readonly PIE: \"pie\"; readonly DONUT: \"donut\"; readonly TREEMAP: \"treemap\"; readonly MOSAIC: \"mosaic\"; readonly WAFFLE: \"waffle\"; }" + ], + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-common.RANGE_MODES", @@ -13310,7 +13789,7 @@ "signature": [ "{ readonly BAR: \"bar\"; readonly LINE: \"line\"; readonly AREA: \"area\"; readonly BAR_STACKED: \"bar_stacked\"; readonly AREA_STACKED: \"area_stacked\"; readonly BAR_HORIZONTAL: \"bar_horizontal\"; readonly BAR_PERCENTAGE_STACKED: \"bar_percentage_stacked\"; readonly BAR_HORIZONTAL_STACKED: \"bar_horizontal_stacked\"; readonly AREA_PERCENTAGE_STACKED: \"area_percentage_stacked\"; readonly BAR_HORIZONTAL_PERCENTAGE_STACKED: \"bar_horizontal_percentage_stacked\"; }" ], - "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -13325,7 +13804,7 @@ "signature": [ "{ readonly LINEAR: \"LINEAR\"; readonly CURVE_MONOTONE_X: \"CURVE_MONOTONE_X\"; readonly CURVE_STEP_AFTER: \"CURVE_STEP_AFTER\"; }" ], - "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -13340,7 +13819,7 @@ "signature": [ "{ readonly AUTO: \"auto\"; readonly LEFT: \"left\"; readonly RIGHT: \"right\"; readonly BOTTOM: \"bottom\"; }" ], - "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", + "path": "src/plugins/visualizations/common/convert_to_lens/constants.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 6e29fc3d03f2..db658a8b5a0b 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: 2022-09-29 +date: 2022-09-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 693 | 12 | 663 | 18 | +| 725 | 12 | 695 | 18 | ## Client From 8e9fc2d9484b375f15fb24fdfd18660d5caefba7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 30 Sep 2022 11:27:46 +0300 Subject: [PATCH 144/185] [Lens] Explore field in Discover (#142199) * [Lens] Explore field in Discover * Remove button for geo_ fields * Open in new tab --- .../field_item.test.tsx | 46 ++++++++++++++++ .../indexpattern_datasource/field_item.tsx | 54 ++++++++++++++++++- .../public/indexpattern_datasource/index.ts | 5 +- .../indexpattern_datasource/indexpattern.tsx | 4 ++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx index 2404d53b8f02..3c6ddbe5e849 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx @@ -9,9 +9,11 @@ import React, { ReactElement } from 'react'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; +import type { DiscoverStart } from '@kbn/discover-plugin/public'; import { InnerFieldItem, FieldItemProps } from './field_item'; import { coreMock } from '@kbn/core/public/mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { findTestSubject } from '@elastic/eui/lib/test'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { IndexPattern } from '../types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -47,6 +49,11 @@ const mockedServices = { fieldFormats: fieldFormatsServiceMock.createStartContract(), charts: chartPluginMock.createSetupContract(), uiSettings: coreMock.createStart().uiSettings, + discover: { + locator: { + getRedirectUrl: jest.fn(() => 'discover_url'), + }, + } as unknown as DiscoverStart, }; const InnerFieldItemWrapper: React.FC = (props) => { @@ -414,4 +421,43 @@ describe('IndexPattern Field Item', () => { expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); expect(wrapper.find(FieldStats).text()).toBe('Analysis is not available for this field.'); }); + + it('should display Explore in discover button', async () => { + const wrapper = await mountWithIntl(); + + await clickField(wrapper, 'bytes'); + + await wrapper.update(); + + const exploreInDiscoverBtn = findTestSubject( + wrapper, + 'lnsFieldListPanel-exploreInDiscover-bytes' + ); + expect(exploreInDiscoverBtn.length).toBe(1); + }); + + it('should not display Explore in discover button for a geo_point field', async () => { + const wrapper = await mountWithIntl( + + ); + + await clickField(wrapper, 'geo_point'); + + await wrapper.update(); + + const exploreInDiscoverBtn = findTestSubject( + wrapper, + 'lnsFieldListPanel-exploreInDiscover-geo_point' + ); + expect(exploreInDiscoverBtn.length).toBe(0); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 29f666a4ff6e..d545d05c8034 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -19,6 +19,7 @@ import { EuiText, EuiTitle, EuiToolTip, + EuiButton, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; @@ -30,7 +31,7 @@ import { DataViewField } from '@kbn/data-views-plugin/common'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { AddFieldFilterHandler, FieldStats } from '@kbn/unified-field-list-plugin/public'; -import { generateFilters } from '@kbn/data-plugin/public'; +import { generateFilters, getEsQueryConfig } from '@kbn/data-plugin/public'; import { DragDrop, DragDropIdentifier } from '../drag_drop'; import { DatasourceDataPanelProps, DataType } from '../types'; import { DOCUMENT_FIELD_NAME } from '../../common'; @@ -41,6 +42,7 @@ import { VisualizeGeoFieldButton } from './visualize_geo_field_button'; import type { LensAppServices } from '../app_plugin/types'; import { debouncedComponent } from '../debounced_component'; import { getFieldType } from './pure_utils'; +import { combineQueryAndFilters } from '../app_plugin/show_underlying_data'; export interface FieldItemProps { core: DatasourceDataPanelProps['core']; @@ -347,6 +349,41 @@ function FieldItemPopoverContents(props: FieldItemProps) { /> ); + const exploreInDiscover = useMemo(() => { + const meta = { + id: indexPattern.id, + columns: [field.name], + filters: { + enabled: { + lucene: [], + kuery: [], + }, + disabled: { + lucene: [], + kuery: [], + }, + }, + }; + const { filters: newFilters, query: newQuery } = combineQueryAndFilters( + query, + filters, + meta, + [indexPattern], + getEsQueryConfig(services.uiSettings) + ); + + if (!services.discover) { + return; + } + return services.discover.locator!.getRedirectUrl({ + dataViewSpec: indexPattern?.spec, + timeRange: services.data.query.timefilter.timefilter.getTime(), + filters: newFilters, + query: newQuery, + columns: meta.columns, + }); + }, [field.name, filters, indexPattern, query, services]); + if (hideDetails) { return panelHeader; } @@ -404,6 +441,21 @@ function FieldItemPopoverContents(props: FieldItemProps) { return params.element; }} /> + {exploreInDiscover && field.type !== 'geo_point' && field.type !== 'geo_shape' && ( + + + {i18n.translate('xpack.lens.indexPattern.fieldExploreInDiscover', { + defaultMessage: 'Explore values in Discover', + })} + + + )} ); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index 4bf33013b328..6b1b052c90b1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -8,6 +8,7 @@ import type { CoreSetup } from '@kbn/core/public'; import { createStartServicesGetter, Storage } from '@kbn/kibana-utils-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { DiscoverStart } from '@kbn/discover-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; @@ -30,6 +31,7 @@ export interface IndexPatternDatasourceSetupPlugins { export interface IndexPatternDatasourceStartPlugins { data: DataPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart; + discover?: DiscoverStart; fieldFormats: FieldFormatsStart; dataViewFieldEditor: IndexPatternFieldEditorStart; dataViews: DataViewsPublicPluginStart; @@ -62,7 +64,7 @@ export class IndexPatternDatasource { const [ coreStart, - { dataViewFieldEditor, uiActions, data, fieldFormats, dataViews, unifiedSearch }, + { dataViewFieldEditor, uiActions, data, fieldFormats, dataViews, unifiedSearch, discover }, ] = await core.getStartServices(); return getIndexPatternDatasource({ @@ -71,6 +73,7 @@ export class IndexPatternDatasource { storage: new Storage(localStorage), data, unifiedSearch, + discover, dataViews, charts, dataViewFieldEditor, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 20d0df1358be..7c083debecb2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -11,6 +11,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import type { CoreStart, SavedObjectReference } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { TimeRange } from '@kbn/es-query'; +import type { DiscoverStart } from '@kbn/discover-plugin/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { flatten, isEqual } from 'lodash'; @@ -130,6 +131,7 @@ export function getIndexPatternDatasource({ storage, data, unifiedSearch, + discover, dataViews, fieldFormats, charts, @@ -140,6 +142,7 @@ export function getIndexPatternDatasource({ storage: IStorageWrapper; data: DataPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart; + discover?: DiscoverStart; dataViews: DataViewsPublicPluginStart; fieldFormats: FieldFormatsStart; charts: ChartsPluginSetup; @@ -282,6 +285,7 @@ export function getIndexPatternDatasource({ fieldFormats, charts, unifiedSearch, + discover, }} > Date: Fri, 30 Sep 2022 10:49:05 +0200 Subject: [PATCH 145/185] [Lens] drag field to annotations to the config panel (#141626) * getCurrentIndexPattern -> getUsedDataView * implement dnd for field --- .../buttons/draggable_dimension_button.tsx | 120 +-- .../buttons/drop_targets_utils.test.tsx | 128 --- .../buttons/drop_targets_utils.tsx | 52 +- .../buttons/empty_dimension_button.tsx | 113 +-- .../config_panel/config_panel.tsx | 2 +- .../editor_frame/config_panel/layer_panel.tsx | 51 +- .../droppable/get_drop_props.ts | 19 +- .../dimension_panel/droppable/mocks.ts | 7 + .../droppable/on_drop_handler.test.ts | 4 + .../droppable/on_drop_handler.ts | 5 +- .../indexpattern_datasource/field_item.tsx | 3 +- .../indexpattern_datasource/indexpattern.tsx | 12 +- .../operations/layer_helpers.test.ts | 2 + .../indexpattern_datasource/pure_utils.ts | 10 +- .../lens/public/mocks/datasource_mock.ts | 1 - .../state_management/lens_slice.test.ts | 2 +- .../public/state_management/lens_slice.ts | 4 +- .../text_based_languages.tsx | 10 +- x-pack/plugins/lens/public/types.ts | 25 +- x-pack/plugins/lens/public/utils.ts | 39 +- .../visualizations/xy/annotations/helpers.tsx | 100 +- .../visualizations/xy/visualization.test.ts | 891 ++++++++++++------ .../visualizations/xy/visualization.tsx | 45 +- 23 files changed, 989 insertions(+), 656 deletions(-) delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.test.tsx diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx index a118183c4906..6e3978a414ec 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx @@ -6,6 +6,7 @@ */ import React, { useMemo, useCallback, useContext, ReactElement } from 'react'; +import { isDraggedField } from '../../../../utils'; import { DragDrop, DragDropIdentifier, DragContext } from '../../../../drag_drop'; import { Datasource, @@ -14,23 +15,20 @@ import { DropType, DatasourceLayers, IndexPatternMap, + DragDropOperation, + Visualization, } from '../../../../types'; import { getCustomDropTarget, getAdditionalClassesOnDroppable, getAdditionalClassesOnEnter, - getDropProps, } from './drop_targets_utils'; export function DraggableDimensionButton({ - layerId, - label, - accessorIndex, - groupIndex, - layerIndex, - columnId, + order, group, onDrop, + activeVisualization, onDragStart, onDragEnd, children, @@ -39,100 +37,82 @@ export function DraggableDimensionButton({ datasourceLayers, registerNewButtonRef, indexPatterns, + target, }: { - layerId: string; - groupIndex: number; - layerIndex: number; + target: DragDropOperation & { + id: string; + humanData: { + label: string; + groupLabel: string; + position: number; + layerNumber: number; + }; + }; + order: [2, number, number, number]; onDrop: (source: DragDropIdentifier, dropTarget: DragDropIdentifier, dropType?: DropType) => void; onDragStart: () => void; onDragEnd: () => void; + activeVisualization: Visualization; group: VisualizationDimensionGroupConfig; - label: string; children: ReactElement; layerDatasource?: Datasource; datasourceLayers: DatasourceLayers; state: unknown; - accessorIndex: number; - columnId: string; registerNewButtonRef: (id: string, instance: HTMLDivElement | null) => void; indexPatterns: IndexPatternMap; }) { const { dragging } = useContext(DragContext); - const sharedDatasource = - !isOperation(dragging) || - datasourceLayers?.[dragging.layerId]?.datasourceId === datasourceLayers?.[layerId]?.datasourceId - ? layerDatasource - : undefined; + let getDropProps; - const dropProps = getDropProps( - { - state, - source: dragging, - target: { - layerId, - columnId, - groupId: group.groupId, - filterOperations: group.filterOperations, - prioritizedOperation: group.prioritizedOperation, - }, - indexPatterns, - }, - sharedDatasource - ); + if (dragging) { + if (!layerDatasource) { + getDropProps = activeVisualization.getDropProps; + } else if ( + isDraggedField(dragging) || + (isOperation(dragging) && + layerDatasource && + datasourceLayers?.[dragging.layerId]?.datasourceId === + datasourceLayers?.[target.layerId]?.datasourceId) + ) { + getDropProps = layerDatasource.getDropProps; + } + } + + const { dropTypes, nextLabel } = getDropProps?.({ + state, + source: dragging, + target, + indexPatterns, + }) || { dropTypes: [], nextLabel: '' }; - const dropTypes = dropProps?.dropTypes; - const nextLabel = dropProps?.nextLabel; const canDuplicate = !!( - dropTypes && - (dropTypes.includes('replace_duplicate_incompatible') || - dropTypes.includes('replace_duplicate_compatible')) + dropTypes.includes('replace_duplicate_incompatible') || + dropTypes.includes('replace_duplicate_compatible') ); const canSwap = !!( - dropTypes && - (dropTypes.includes('swap_incompatible') || dropTypes.includes('swap_compatible')) + dropTypes.includes('swap_incompatible') || dropTypes.includes('swap_compatible') ); const canCombine = Boolean( - dropTypes && - (dropTypes.includes('combine_compatible') || - dropTypes.includes('field_combine') || - dropTypes.includes('combine_incompatible')) + dropTypes.includes('combine_compatible') || + dropTypes.includes('field_combine') || + dropTypes.includes('combine_incompatible') ); const value = useMemo( () => ({ - columnId, - groupId: group.groupId, - layerId, - id: columnId, - filterOperations: group.filterOperations, + ...target, humanData: { + ...target.humanData, canSwap, canDuplicate, canCombine, - label, - groupLabel: group.groupLabel, - position: accessorIndex + 1, nextLabel: nextLabel || '', - layerNumber: layerIndex + 1, }, }), - [ - columnId, - group.groupId, - accessorIndex, - layerId, - label, - group.groupLabel, - nextLabel, - group.filterOperations, - canDuplicate, - canSwap, - canCombine, - layerIndex, - ] + [target, nextLabel, canDuplicate, canSwap, canCombine] ); const reorderableGroup = useMemo( @@ -144,8 +124,8 @@ export function DraggableDimensionButton({ ); const registerNewButtonRefMemoized = useCallback( - (el) => registerNewButtonRef(columnId, el), - [registerNewButtonRef, columnId] + (el) => registerNewButtonRef(target.columnId, el), + [registerNewButtonRef, target.columnId] ); const handleOnDrop = useCallback( @@ -162,7 +142,7 @@ export function DraggableDimensionButton({ getCustomDropTarget={getCustomDropTarget} getAdditionalClassesOnEnter={getAdditionalClassesOnEnter} getAdditionalClassesOnDroppable={getAdditionalClassesOnDroppable} - order={[2, layerIndex, groupIndex, accessorIndex]} + order={order} draggable dragType={isOperation(dragging) ? 'move' : 'copy'} dropTypes={dropTypes} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.test.tsx deleted file mode 100644 index 17907ac19c4b..000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.test.tsx +++ /dev/null @@ -1,128 +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 { getDropProps } from './drop_targets_utils'; -import { createMockDatasource } from '../../../../mocks'; - -describe('getDropProps', () => { - it('should run datasource getDropProps if exists', () => { - const mockDatasource = createMockDatasource('testDatasource'); - getDropProps( - { - state: 'datasourceState', - target: { - columnId: 'col1', - groupId: 'x', - layerId: 'first', - filterOperations: () => true, - }, - source: { - columnId: 'col1', - groupId: 'x', - layerId: 'first', - id: 'annotationColumn2', - humanData: { label: 'Event' }, - }, - indexPatterns: {}, - }, - mockDatasource - ); - expect(mockDatasource.getDropProps).toHaveBeenCalled(); - }); - describe('no datasource', () => { - it('returns reorder for the same group existing columns', () => { - expect( - getDropProps({ - state: 'datasourceState', - target: { - columnId: 'annotationColumn', - groupId: 'xAnnotations', - layerId: 'second', - filterOperations: () => true, - }, - source: { - columnId: 'annotationColumn2', - groupId: 'xAnnotations', - layerId: 'second', - id: 'annotationColumn2', - humanData: { label: 'Event' }, - }, - indexPatterns: {}, - }) - ).toEqual({ dropTypes: ['reorder'] }); - }); - it('returns duplicate for the same group existing column and not existing column', () => { - expect( - getDropProps({ - state: 'datasourceState', - target: { - columnId: 'annotationColumn', - groupId: 'xAnnotations', - layerId: 'second', - isNewColumn: true, - filterOperations: () => true, - }, - source: { - columnId: 'annotationColumn2', - groupId: 'xAnnotations', - layerId: 'second', - id: 'annotationColumn2', - humanData: { label: 'Event' }, - }, - indexPatterns: {}, - }) - ).toEqual({ dropTypes: ['duplicate_compatible'] }); - }); - it('returns replace_duplicate and replace for replacing to different layer', () => { - expect( - getDropProps({ - state: 'datasourceState', - target: { - columnId: 'annotationColumn', - groupId: 'xAnnotations', - layerId: 'first', - filterOperations: () => true, - }, - source: { - columnId: 'annotationColumn2', - groupId: 'xAnnotations', - layerId: 'second', - id: 'annotationColumn2', - humanData: { label: 'Event' }, - }, - indexPatterns: {}, - }) - ).toEqual({ - dropTypes: ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'], - }); - }); - it('returns duplicate and move for replacing to different layer for empty column', () => { - expect( - getDropProps({ - state: 'datasourceState', - target: { - columnId: 'annotationColumn', - groupId: 'xAnnotations', - layerId: 'first', - isNewColumn: true, - filterOperations: () => true, - }, - source: { - columnId: 'annotationColumn2', - groupId: 'xAnnotations', - layerId: 'second', - id: 'annotationColumn2', - humanData: { label: 'Event' }, - }, - indexPatterns: {}, - }) - ).toEqual({ - dropTypes: ['move_compatible', 'duplicate_compatible'], - }); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx index 5f3fd2d4a73b..3094a07cf329 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx @@ -9,12 +9,10 @@ import React from 'react'; import classNames from 'classnames'; import { EuiIcon, EuiFlexItem, EuiFlexGroup, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DragDropIdentifier, DraggingIdentifier } from '../../../../drag_drop'; +import { DragDropIdentifier } from '../../../../drag_drop'; import { - Datasource, DropType, FramePublicAPI, - GetDropPropsArgs, isOperation, Visualization, DragDropOperation, @@ -140,53 +138,6 @@ export const getAdditionalClassesOnDroppable = (dropType?: string) => { } }; -const isOperationFromCompatibleGroup = (op1?: DraggingIdentifier, op2?: DragDropOperation) => { - return ( - isOperation(op1) && - isOperation(op2) && - op1.columnId !== op2.columnId && - op1.groupId === op2.groupId && - op1.layerId !== op2.layerId - ); -}; - -export const isOperationFromTheSameGroup = (op1?: DraggingIdentifier, op2?: DragDropOperation) => { - return ( - isOperation(op1) && - isOperation(op2) && - op1.columnId !== op2.columnId && - op1.groupId === op2.groupId && - op1.layerId === op2.layerId - ); -}; - -export function getDropPropsForSameGroup( - isNewColumn?: boolean -): { dropTypes: DropType[]; nextLabel?: string } | undefined { - return !isNewColumn ? { dropTypes: ['reorder'] } : { dropTypes: ['duplicate_compatible'] }; -} - -export const getDropProps = ( - dropProps: GetDropPropsArgs, - sharedDatasource?: Datasource -): { dropTypes: DropType[]; nextLabel?: string } | undefined => { - if (sharedDatasource) { - return sharedDatasource?.getDropProps(dropProps); - } else { - if (isOperationFromTheSameGroup(dropProps.source, dropProps.target)) { - return getDropPropsForSameGroup(dropProps.target.isNewColumn); - } - if (isOperationFromCompatibleGroup(dropProps.source, dropProps.target)) { - return { - dropTypes: dropProps.target.isNewColumn - ? ['move_compatible', 'duplicate_compatible'] - : ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'], - }; - } - } - return; -}; - export interface OnVisDropProps { prevState: T; target: DragDropOperation; @@ -215,7 +166,6 @@ export function onDropForVisualization( frame, }); - // remove source if ( isOperation(source) && (dropType === 'move_compatible' || diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index b6d7e58b0f7e..8b1fe4082b31 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -9,6 +9,7 @@ import React, { useMemo, useState, useEffect, useContext } from 'react'; import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import { isDraggedField } from '../../../../utils'; import { generateId } from '../../../../id_generator'; import { DragDrop, DragDropIdentifier, DragContext } from '../../../../drag_drop'; @@ -19,16 +20,10 @@ import { DatasourceLayers, isOperation, IndexPatternMap, + DragDropOperation, + Visualization, } from '../../../../types'; -import { - getCustomDropTarget, - getAdditionalClassesOnDroppable, - getDropProps, -} from './drop_targets_utils'; - -const label = i18n.translate('xpack.lens.indexPattern.emptyDimensionButton', { - defaultMessage: 'Empty dimension', -}); +import { getCustomDropTarget, getAdditionalClassesOnDroppable } from './drop_targets_utils'; interface EmptyButtonProps { columnId: string; @@ -106,91 +101,81 @@ export function EmptyDimensionButton({ group, layerDatasource, state, - layerId, - groupIndex, - layerIndex, onClick, onDrop, datasourceLayers, indexPatterns, + activeVisualization, + order, + target, }: { - layerId: string; - groupIndex: number; - layerIndex: number; - onDrop: (source: DragDropIdentifier, dropTarget: DragDropIdentifier, dropType?: DropType) => void; - onClick: (id: string) => void; + order: [2, number, number, number]; group: VisualizationDimensionGroupConfig; layerDatasource?: Datasource; datasourceLayers: DatasourceLayers; state: unknown; + onDrop: (source: DragDropIdentifier, dropTarget: DragDropIdentifier, dropType?: DropType) => void; + onClick: (id: string) => void; indexPatterns: IndexPatternMap; + activeVisualization: Visualization; + target: Omit & { + humanData: { + groupLabel: string; + position: number; + layerNumber: number; + label: string; + }; + }; }) { const { dragging } = useContext(DragContext); - const sharedDatasource = - !isOperation(dragging) || - datasourceLayers?.[dragging.layerId]?.datasourceId === datasourceLayers?.[layerId]?.datasourceId - ? layerDatasource - : undefined; - const itemIndex = group.accessors.length; + let getDropProps; + + if (dragging) { + if (!layerDatasource) { + getDropProps = activeVisualization.getDropProps; + } else if ( + isDraggedField(dragging) || + (isOperation(dragging) && + layerDatasource && + datasourceLayers?.[dragging.layerId]?.datasourceId === + datasourceLayers?.[target.layerId]?.datasourceId) + ) { + getDropProps = layerDatasource.getDropProps; + } + } const [newColumnId, setNewColumnId] = useState(generateId()); useEffect(() => { setNewColumnId(generateId()); - }, [itemIndex]); - - const dropProps = getDropProps( - { - state, - source: dragging, - target: { - layerId, - columnId: newColumnId, - groupId: group.groupId, - filterOperations: group.filterOperations, - prioritizedOperation: group.prioritizedOperation, - isNewColumn: true, - }, - indexPatterns, - }, - sharedDatasource - ); + }, [group.accessors.length]); - const dropTypes = dropProps?.dropTypes; - const nextLabel = dropProps?.nextLabel; + const { dropTypes, nextLabel } = getDropProps?.({ + state, + source: dragging, + target: { + ...target, + columnId: newColumnId, + }, + indexPatterns, + }) || { dropTypes: [], nextLabel: '' }; const canDuplicate = !!( - dropTypes && - (dropTypes.includes('duplicate_compatible') || dropTypes.includes('duplicate_incompatible')) + dropTypes.includes('duplicate_compatible') || dropTypes.includes('duplicate_incompatible') ); const value = useMemo( () => ({ + ...target, columnId: newColumnId, - groupId: group.groupId, - layerId, - filterOperations: group.filterOperations, id: newColumnId, humanData: { - label, - groupLabel: group.groupLabel, - position: itemIndex + 1, + ...target.humanData, nextLabel: nextLabel || '', canDuplicate, - layerNumber: layerIndex + 1, }, }), - [ - newColumnId, - group.groupId, - layerId, - group.groupLabel, - group.filterOperations, - itemIndex, - nextLabel, - canDuplicate, - layerIndex, - ] + [newColumnId, target, nextLabel, canDuplicate] ); const handleOnDrop = React.useCallback( @@ -209,7 +194,7 @@ export function EmptyDimensionButton({ layerDatasource.getUsedDataView(layerDatasourceState, layer)), - defaultDataView: layerDatasource.getCurrentIndexPatternId(layerDatasourceState), + defaultDataView: layerDatasource.getUsedDataView(layerDatasourceState), } as ActionExecutionContext); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index cc748df7c3ec..e40281fa1f3e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -458,18 +458,34 @@ export function LayerPanel( const { columnId } = accessorConfig; return ( setHideTooltip(true)} onDragEnd={() => setHideTooltip(false)} onDrop={onDrop} @@ -567,10 +583,27 @@ export function LayerPanel( {group.supportsMoreColumns ? ( !op.isBucketed, id: 'col1', humanData: { label: 'Column 1' }, + indexPatternId: 'first', }, numericalOnly: { layerId: 'first', @@ -257,6 +259,7 @@ export const mockedDndOperations = { filterOperations: (op: OperationMetadata) => op.dataType === 'number', id: 'col1', humanData: { label: 'Column 1' }, + indexPatternId: 'first', }, bucket: { columnId: 'col2', @@ -265,6 +268,7 @@ export const mockedDndOperations = { id: 'col2', humanData: { label: 'Column 2' }, filterOperations: (op: OperationMetadata) => op.isBucketed, + indexPatternId: 'first', }, staticValue: { columnId: 'col1', @@ -273,6 +277,7 @@ export const mockedDndOperations = { id: 'col1', humanData: { label: 'Column 2' }, filterOperations: (op: OperationMetadata) => !!op.isStaticValue, + indexPatternId: 'first', }, bucket2: { columnId: 'col3', @@ -282,6 +287,7 @@ export const mockedDndOperations = { humanData: { label: '', }, + indexPatternId: 'first', }, metricC: { columnId: 'col4', @@ -292,5 +298,6 @@ export const mockedDndOperations = { label: '', }, filterOperations: (op: OperationMetadata) => !op.isBucketed, + indexPatternId: 'first', }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.test.ts index 6156be357003..3b468181db6d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.test.ts @@ -1562,12 +1562,14 @@ describe('IndexPatternDimensionEditorPanel: onDrop', () => { groupId: 'x', layerId: 'first', filterOperations: (op: OperationMetadata) => op.isBucketed, + indexPatternId: 'indexPattern1', }, target: { filterOperations: (op: OperationMetadata) => op.isBucketed, columnId: 'newCol', groupId: 'x', layerId: 'second', + indexPatternId: 'indexPattern1', }, dimensionGroups: defaultDimensionGroups, dropType: 'move_compatible', @@ -2161,6 +2163,7 @@ describe('IndexPatternDimensionEditorPanel: onDrop', () => { groupId: 'y', layerId: 'second', filterOperations: (op) => !op.isBucketed, + indexPatternId: 'test', }, }; @@ -2224,6 +2227,7 @@ describe('IndexPatternDimensionEditorPanel: onDrop', () => { groupId: 'y', layerId: 'second', filterOperations: (op) => !op.isBucketed, + indexPatternId: 'test', }, }) ).toEqual(true); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts index 8ea027a3da98..77444e8ae59e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { isDraggedField } from '../../../utils'; import { DatasourceDimensionDropHandlerProps, DragDropOperation, @@ -12,6 +13,7 @@ import { isOperation, StateSetter, VisualizationDimensionGroupConfig, + DraggedField, } from '../../../types'; import { insertOrReplaceColumn, @@ -25,9 +27,8 @@ import { deleteColumnInLayers, } from '../../operations'; import { mergeLayer, mergeLayers } from '../../state_helpers'; -import { isDraggedField } from '../../pure_utils'; import { getNewOperation, getField } from './get_drop_props'; -import { IndexPatternPrivateState, DraggedField, DataViewDragDropOperation } from '../../types'; +import { IndexPatternPrivateState, DataViewDragDropOperation } from '../../types'; interface DropHandlerProps { state: IndexPatternPrivateState; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index d545d05c8034..a91286881197 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -33,10 +33,9 @@ import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { AddFieldFilterHandler, FieldStats } from '@kbn/unified-field-list-plugin/public'; import { generateFilters, getEsQueryConfig } from '@kbn/data-plugin/public'; import { DragDrop, DragDropIdentifier } from '../drag_drop'; -import { DatasourceDataPanelProps, DataType } from '../types'; +import { DatasourceDataPanelProps, DataType, DraggedField } from '../types'; import { DOCUMENT_FIELD_NAME } from '../../common'; import type { IndexPattern, IndexPatternField } from '../types'; -import type { DraggedField } from './types'; import { LensFieldIcon } from '../shared_components/field_picker/lens_field_icon'; import { VisualizeGeoFieldButton } from './visualize_geo_field_button'; import type { LensAppServices } from '../app_plugin/types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 7c083debecb2..c16ced9ab78f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -69,7 +69,8 @@ import { isColumnInvalid, cloneLayer, } from './utils'; -import { normalizeOperationDataType, isDraggedField } from './pure_utils'; +import { isDraggedField } from '../utils'; +import { normalizeOperationDataType } from './pure_utils'; import { LayerPanel } from './layerpanel'; import { DateHistogramIndexPatternColumn, @@ -179,10 +180,6 @@ export function getIndexPatternDatasource({ return extractReferences(state); }, - getCurrentIndexPatternId(state: IndexPatternPrivateState) { - return state.currentIndexPatternId; - }, - insertLayer(state: IndexPatternPrivateState, newLayerId: string) { return { ...state, @@ -778,7 +775,10 @@ export function getIndexPatternDatasource({ injectReferences(persistableState1, references1), injectReferences(persistableState2, references2) ), - getUsedDataView: (state: IndexPatternPrivateState, layerId: string) => { + getUsedDataView: (state: IndexPatternPrivateState, layerId?: string) => { + if (!layerId) { + return state.currentIndexPatternId; + } return state.layers[layerId].indexPatternId; }, getUsedDataViews: (state) => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 2df8300eb648..1a77cd253424 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -190,6 +190,7 @@ describe('state_helpers', () => { layerId: 'layer', dataView: indexPattern, filterOperations: () => true, + indexPatternId: '1', }, target: { columnId: 'copy', @@ -197,6 +198,7 @@ describe('state_helpers', () => { dataView: indexPattern, layerId: 'layer', filterOperations: () => true, + indexPatternId: '1', }, shouldDeleteSource: false, }).layer diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/pure_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/pure_utils.ts index e1fd78e0b171..39b4bcdf4922 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/pure_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/pure_utils.ts @@ -6,7 +6,7 @@ */ import type { DataType, IndexPattern, IndexPatternField } from '../types'; -import type { DraggedField, IndexPatternLayer } from './types'; +import type { IndexPatternLayer } from './types'; import type { BaseIndexPatternColumn, FieldBasedIndexPatternColumn, @@ -53,11 +53,3 @@ export function sortByField(columns: C[]) { return column1.operationType.localeCompare(column2.operationType); }); } - -export function isDraggedField(fieldCandidate: unknown): fieldCandidate is DraggedField { - return ( - typeof fieldCandidate === 'object' && - fieldCandidate !== null && - ['id', 'field', 'indexPatternId'].every((prop) => prop in fieldCandidate) - ); -} diff --git a/x-pack/plugins/lens/public/mocks/datasource_mock.ts b/x-pack/plugins/lens/public/mocks/datasource_mock.ts index 3d169b643c2c..65d001a726b8 100644 --- a/x-pack/plugins/lens/public/mocks/datasource_mock.ts +++ b/x-pack/plugins/lens/public/mocks/datasource_mock.ts @@ -41,7 +41,6 @@ export function createMockDatasource(id: string): DatasourceMock { initialize: jest.fn((_state?) => {}), renderDataPanel: jest.fn(), renderLayerPanel: jest.fn(), - getCurrentIndexPatternId: jest.fn(), toExpression: jest.fn((_frame, _state, _indexPatterns) => null), insertLayer: jest.fn((_state, _newLayerId) => ({})), removeLayer: jest.fn((_state, _layerId) => {}), diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts index fc536b30ddac..f83786238f62 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts @@ -279,7 +279,7 @@ describe('lensSlice', () => { removeLayer: (layerIds: unknown, layerId: string) => (layerIds as string[]).filter((id: string) => id !== layerId), insertLayer: (layerIds: unknown, layerId: string) => [...(layerIds as string[]), layerId], - getCurrentIndexPatternId: jest.fn(() => 'indexPattern1'), + getUsedDataView: jest.fn(() => 'indexPattern1'), }; }; const datasourceStates = { diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index 725c60bfc22c..38aee718a536 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -377,7 +377,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { ); state.stagedPreview = undefined; // reuse the activeDatasource current dataView id for the moment - const currentDataViewsId = activeDataSource.getCurrentIndexPatternId( + const currentDataViewsId = activeDataSource.getUsedDataView( state.datasourceStates[state.activeDatasourceId!].state ); state.visualization.state = @@ -928,7 +928,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { const activeVisualization = visualizationMap[state.visualization.activeId]; const activeDatasource = datasourceMap[state.activeDatasourceId]; // reuse the active datasource dataView id for the new layer - const currentDataViewsId = activeDatasource.getCurrentIndexPatternId( + const currentDataViewsId = activeDatasource.getUsedDataView( state.datasourceStates[state.activeDatasourceId!].state ); const visualizationState = activeVisualization.appendLayer!( diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx index 5a03500c76fb..4857d3eed3e3 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx @@ -224,10 +224,6 @@ export function getTextBasedLanguagesDatasource({ getLayers(state: TextBasedLanguagesPrivateState) { return state && state.layers ? Object.keys(state?.layers) : []; }, - getCurrentIndexPatternId(state: TextBasedLanguagesPrivateState) { - const layers = Object.values(state.layers); - return layers?.[0]?.index; - }, isTimeBased: (state, indexPatterns) => { if (!state) return false; const { layers } = state; @@ -238,7 +234,11 @@ export function getTextBasedLanguagesDatasource({ }) ); }, - getUsedDataView: (state: TextBasedLanguagesPrivateState, layerId: string) => { + getUsedDataView: (state: TextBasedLanguagesPrivateState, layerId?: string) => { + if (!layerId) { + const layers = Object.values(state.layers); + return layers?.[0]?.index; + } return state.layers[layerId].index; }, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 32fc21cc4a61..486ddef88ee9 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -228,14 +228,7 @@ export type VisualizeEditorContext = { export interface GetDropPropsArgs { state: T; source?: DraggingIdentifier; - target: { - layerId: string; - groupId: string; - columnId: string; - filterOperations: (meta: OperationMetadata) => boolean; - prioritizedOperation?: string; - isNewColumn?: boolean; - }; + target: DragDropOperation; indexPatterns: IndexPatternMap; } @@ -258,7 +251,6 @@ export interface Datasource { // Given the current state, which parts should be saved? getPersistableState: (state: T) => { state: P; savedObjectReferences: SavedObjectReference[] }; - getCurrentIndexPatternId: (state: T) => string; getUnifiedSearchErrors?: (state: T) => Error[]; insertLayer: (state: T, newLayerId: string) => T; @@ -441,7 +433,7 @@ export interface Datasource { /** * Get the used DataView value from state */ - getUsedDataView: (state: T, layerId: string) => string; + getUsedDataView: (state: T, layerId?: string) => string; /** * Get all the used DataViews from state */ @@ -582,8 +574,16 @@ export interface DragDropOperation { groupId: string; columnId: string; filterOperations: (operation: OperationMetadata) => boolean; + indexPatternId?: string; + isNewColumn?: boolean; + prioritizedOperation?: string; } +export type DraggedField = DragDropIdentifier & { + field: IndexPatternField; + indexPatternId: string; +}; + export function isOperation(operationCandidate: unknown): operationCandidate is DragDropOperation { return ( typeof operationCandidate === 'object' && @@ -892,6 +892,7 @@ export interface Visualization { */ initialize: (addNewLayer: () => string, state?: T, mainPalette?: PaletteOutput) => T; + getUsedDataView?: (state: T, layerId: string) => string | undefined; /** * Retrieve the used DataViews in the visualization */ @@ -1022,6 +1023,10 @@ export interface Visualization { group?: VisualizationDimensionGroupConfig; }) => T; + getDropProps?: ( + dropProps: GetDropPropsArgs + ) => { dropTypes: DropType[]; nextLabel?: string } | undefined; + /** * Additional editor that gets rendered inside the dimension popover. * This can be used to configure dimension-specific options diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 8f25379c0e21..181ea104ffa7 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -15,15 +15,19 @@ import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public' import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import { BrushTriggerEvent, ClickTriggerEvent } from '@kbn/charts-plugin/public'; import type { Document } from './persistence/saved_object_store'; -import type { +import { Datasource, DatasourceMap, Visualization, IndexPatternMap, IndexPatternRef, + DraggedField, + DragDropOperation, + isOperation, } from './types'; import type { DatasourceStates, VisualizationState } from './state_management'; import { IndexPatternServiceAPI } from './data_views_service/service'; +import { DraggingIdentifier } from './drag_drop'; export function getVisualizeGeoFieldMessage(fieldType: string) { return i18n.translate('xpack.lens.visualizeGeoFieldMessage', { @@ -126,7 +130,7 @@ export function getIndexPatternsIds({ const references: SavedObjectReference[] = []; Object.entries(activeDatasources).forEach(([id, datasource]) => { const { savedObjectReferences } = datasource.getPersistableState(datasourceStates[id].state); - const indexPatternId = datasource.getCurrentIndexPatternId(datasourceStates[id].state); + const indexPatternId = datasource.getUsedDataView(datasourceStates[id].state); currentIndexPatternId = indexPatternId; references.push(...savedObjectReferences); }); @@ -242,3 +246,34 @@ export function renewIDs( */ export const DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS = 'lensDontCloseDimensionContainerOnClick'; + +export function isDraggedField(fieldCandidate: unknown): fieldCandidate is DraggedField { + return ( + typeof fieldCandidate === 'object' && + fieldCandidate !== null && + ['id', 'field', 'indexPatternId'].every((prop) => prop in fieldCandidate) + ); +} + +export const isOperationFromCompatibleGroup = ( + op1?: DraggingIdentifier, + op2?: DragDropOperation +) => { + return ( + isOperation(op1) && + isOperation(op2) && + op1.columnId !== op2.columnId && + op1.groupId === op2.groupId && + op1.layerId !== op2.layerId + ); +}; + +export const isOperationFromTheSameGroup = (op1?: DraggingIdentifier, op2?: DragDropOperation) => { + return ( + isOperation(op1) && + isOperation(op2) && + op1.columnId !== op2.columnId && + op1.groupId === op2.groupId && + op1.layerId === op2.layerId + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index baaed78ec023..3c54d67c4913 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -10,10 +10,12 @@ import moment from 'moment'; import { defaultAnnotationColor, defaultAnnotationRangeColor, + isQueryAnnotationConfig, isRangeAnnotationConfig, } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { IconChartBarAnnotations } from '@kbn/chart-icons'; +import { isDraggedField } from '../../../utils'; import { layerTypes } from '../../../../common'; import type { FramePublicAPI, Visualization } from '../../../types'; import { isHorizontalChart } from '../state_helpers'; @@ -125,7 +127,7 @@ export const getAnnotationsSupportedLayer = ( }; }; -const getDefaultAnnotationConfig = (id: string, timestamp: string): EventAnnotationConfig => ({ +const getDefaultManualAnnotation = (id: string, timestamp: string): EventAnnotationConfig => ({ label: defaultAnnotationLabel, type: 'manual', key: { @@ -136,13 +138,32 @@ const getDefaultAnnotationConfig = (id: string, timestamp: string): EventAnnotat id, }); +const getDefaultQueryAnnotation = ( + id: string, + fieldName: string, + timeField: string +): EventAnnotationConfig => ({ + filter: { + type: 'kibana_query', + query: `${fieldName}: *`, + language: 'kuery', + }, + timeField, + type: 'query', + key: { + type: 'point_in_time', + }, + id, + label: `${fieldName}: *`, +}); + const createCopiedAnnotation = ( newId: string, timestamp: string, source?: EventAnnotationConfig ): EventAnnotationConfig => { if (!source) { - return getDefaultAnnotationConfig(newId, timestamp); + return getDefaultManualAnnotation(newId, timestamp); } return { ...source, @@ -158,17 +179,78 @@ export const onAnnotationDrop: Visualization['onDrop'] = ({ dropType, }) => { const targetLayer = prevState.layers.find((l) => l.layerId === target.layerId); - const sourceLayer = prevState.layers.find((l) => l.layerId === source.layerId); - if ( - !targetLayer || - !isAnnotationsLayer(targetLayer) || - !sourceLayer || - !isAnnotationsLayer(sourceLayer) - ) { + if (!targetLayer || !isAnnotationsLayer(targetLayer)) { return prevState; } const targetAnnotation = targetLayer.annotations.find(({ id }) => id === target.columnId); + const targetDataView = frame.dataViews.indexPatterns[targetLayer.indexPatternId]; + + if (isDraggedField(source)) { + const timeField = targetDataView.timeFieldName; + switch (dropType) { + case 'field_add': + if (targetAnnotation || !timeField) { + return prevState; + } + return { + ...prevState, + layers: prevState.layers.map( + (l): XYLayerConfig => + l.layerId === target.layerId + ? { + ...targetLayer, + annotations: [ + ...targetLayer.annotations, + getDefaultQueryAnnotation(target.columnId, source.field.name, timeField), + ], + } + : l + ), + }; + case 'field_replace': + if (!targetAnnotation || !timeField) { + return prevState; + } + + return { + ...prevState, + layers: prevState.layers.map( + (l): XYLayerConfig => + l.layerId === target.layerId + ? { + ...targetLayer, + annotations: [ + ...targetLayer.annotations.map((a) => + a === targetAnnotation + ? { + ...targetAnnotation, + ...getDefaultQueryAnnotation( + target.columnId, + source.field.name, + timeField + ), + } + : a + ), + ], + } + : l + ), + }; + } + + return prevState; + } + + const sourceLayer = prevState.layers.find((l) => l.layerId === source.layerId); + if (!sourceLayer || !isAnnotationsLayer(sourceLayer)) { + return prevState; + } const sourceAnnotation = sourceLayer.annotations.find(({ id }) => id === source.columnId); + const sourceDataView = frame.dataViews.indexPatterns[sourceLayer.indexPatternId]; + if (sourceDataView !== targetDataView && isQueryAnnotationConfig(sourceAnnotation)) { + return prevState; + } switch (dropType) { case 'reorder': if (!targetAnnotation || !sourceAnnotation || source.layerId !== target.layerId) { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 556a89c9a855..c62d2c1195e5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -338,7 +338,11 @@ describe('xy_visualization', () => { let frame: ReturnType; beforeEach(() => { - frame = createMockFramePublicAPI(); + frame = createMockFramePublicAPI({ + dataViews: createMockDataViewsState({ + indexPatterns: { indexPattern1: createMockedIndexPattern() }, + }), + }); mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ @@ -494,309 +498,650 @@ describe('xy_visualization', () => { ], }); }); - it('should copy previous column if passed and assign a new id', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, + + describe('getDropProps', () => { + it('dragging operation: returns reorder for the same group existing columns', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'second', + filterOperations: () => true, + indexPatternId: '1', + }, + source: { + columnId: 'annotationColumn2', + groupId: 'xAnnotations', + layerId: 'second', + id: 'annotationColumn2', + humanData: { label: 'Event' }, + indexPatternId: '1', + }, + indexPatterns: {}, + }) + ).toEqual({ dropTypes: ['reorder'] }); + }); + it('dragging operation: returns duplicate for the same group existing column and not existing column', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'second', + isNewColumn: true, + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + source: { + columnId: 'annotationColumn2', + groupId: 'xAnnotations', + layerId: 'second', + id: 'annotationColumn2', + humanData: { label: 'Event' }, + indexPatternId: 'indexPattern1', + }, + indexPatterns: {}, + }) + ).toEqual({ dropTypes: ['duplicate_compatible'] }); + }); + it('dragging operation: returns replace_duplicate and replace for replacing to different layer', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + filterOperations: () => true, + indexPatternId: '1', + }, + source: { + columnId: 'annotationColumn2', + groupId: 'xAnnotations', + layerId: 'second', + id: 'annotationColumn2', + humanData: { label: 'Event' }, + indexPatternId: '1', + }, + indexPatterns: {}, + }) + ).toEqual({ + dropTypes: ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'], + }); + }); + it('dragging operation: returns duplicate and move for replacing to different layer for empty column', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + isNewColumn: true, + indexPatternId: 'indexPattern1', + filterOperations: () => true, + }, + source: { + columnId: 'annotationColumn2', + groupId: 'xAnnotations', + layerId: 'second', + id: 'annotationColumn2', + humanData: { label: 'Event' }, + indexPatternId: 'indexPattern1', + }, + indexPatterns: {}, + }) + ).toEqual({ + dropTypes: ['move_compatible', 'duplicate_compatible'], + }); + }); + it('dragging operation: does not allow to drop for different operations on different data views', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + isNewColumn: true, + indexPatternId: 'indexPattern1', + filterOperations: () => true, + }, + source: { + columnId: 'annotationColumn2', + groupId: 'xAnnotations', + layerId: 'second', + id: 'annotationColumn2', + humanData: { label: 'Event' }, + indexPatternId: 'indexPattern2', + }, + indexPatterns: {}, + }) + ).toEqual(undefined); + }); + it('dragging field: should add a new dimension when dragged to a new dimension', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + isNewColumn: true, + indexPatternId: 'indexPattern1', + filterOperations: () => true, + }, + source: { + field: { + name: 'agent.keyword', + displayName: 'agent.keyword', }, - ], - }, - dropType: 'duplicate_compatible', - source: { - layerId: 'annotation', - groupId: 'xAnnotation', - columnId: 'an2', - id: 'an2', - humanData: { label: 'an2' }, - }, - target: { - layerId: 'annotation', - groupId: 'xAnnotation', - columnId: 'newColId', - filterOperations: Boolean, - }, - }).layers[0] - ).toEqual({ - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], - ignoreGlobalFilters: true, + indexPatternId: 'indexPattern1', + id: 'agent.keyword', + humanData: { + label: 'agent.keyword', + position: 2, + }, + }, + indexPatterns: {}, + }) + ).toEqual({ dropTypes: ['field_add'] }); }); - }); - it('should reorder a dimension to a annotation layer', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation, exampleAnnotation2], - ignoreGlobalFilters: true, + it('dragging field: should replace an existing dimension when dragged to a dimension', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + indexPatternId: 'indexPattern1', + filterOperations: () => true, + }, + source: { + field: { + name: 'agent.keyword', + displayName: 'agent.keyword', }, - ], - }, - source: { - layerId: 'annotation', - groupId: 'xAnnotation', - columnId: 'an2', - id: 'an2', - humanData: { label: 'label' }, - filterOperations: () => true, - }, - target: { - layerId: 'annotation', - groupId: 'xAnnotation', - columnId: 'an1', - filterOperations: () => true, - }, - dropType: 'reorder', - }).layers[0] - ).toEqual({ - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2, exampleAnnotation], - ignoreGlobalFilters: true, + indexPatternId: 'indexPattern1', + id: 'agent.keyword', + humanData: { + label: 'agent.keyword', + position: 2, + }, + }, + indexPatterns: {}, + }) + ).toEqual({ dropTypes: ['field_replace'] }); + }); + it('dragging field: should not allow to drop when data view conflict', () => { + expect( + xyVisualization.getDropProps?.({ + state: 'datasourceState', + target: { + columnId: 'annotationColumn', + groupId: 'xAnnotations', + layerId: 'first', + indexPatternId: 'indexPattern1', + filterOperations: () => true, + }, + source: { + field: { + name: 'agent.keyword', + displayName: 'agent.keyword', + }, + indexPatternId: 'indexPattern2', + id: 'agent.keyword', + humanData: { + label: 'agent.keyword', + position: 2, + }, + }, + indexPatterns: {}, + }) + ).toEqual(undefined); }); }); - it('should duplicate the annotations and replace the target in another annotation layer', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, + describe('onDrop', () => { + it('dragging field: should add a new dimension when dragged to a new dimension', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + dropType: 'field_add', + source: { + field: { + name: 'agent.keyword', }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, + indexPatternId: 'indexPattern1', + id: 'agent.keyword', + humanData: { + label: 'agent.keyword', + position: 2, }, - ], - }, - source: { - layerId: 'first', - groupId: 'xAnnotation', - columnId: 'an1', - id: 'an1', - humanData: { label: 'label' }, - filterOperations: () => true, - }, - target: { - layerId: 'second', - groupId: 'xAnnotation', - columnId: 'an2', - filterOperations: () => true, - }, - dropType: 'replace_duplicate_compatible', - }).layers - ).toEqual([ - { - layerId: 'first', + }, + target: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'newColId', + filterOperations: Boolean, + indexPatternId: 'indexPattern1', + }, + }).layers[0] + ).toEqual({ + layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], + annotations: [ + exampleAnnotation2, + { + filter: { + language: 'kuery', + query: 'agent.keyword: *', + type: 'kibana_query', + }, + id: 'newColId', + key: { + type: 'point_in_time', + }, + label: 'agent.keyword: *', + timeField: 'timestamp', + type: 'query', + }, + ], ignoreGlobalFilters: true, - }, - { - layerId: 'second', + }); + }); + it('dragging field: should replace an existing dimension when dragged to a dimension', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + ], + }, + dropType: 'field_replace', + source: { + field: { + name: 'agent.keyword', + }, + indexPatternId: 'indexPattern1', + id: 'agent.keyword', + humanData: { + label: 'agent.keyword', + position: 2, + }, + }, + target: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'an1', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + }).layers[0] + ).toEqual({ + layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', - annotations: [{ ...exampleAnnotation, id: 'an2' }], - ignoreGlobalFilters: true, - }, - ]); - }); - it('should swap the annotations between layers', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, + annotations: [ + { + filter: { + language: 'kuery', + query: 'agent.keyword: *', + type: 'kibana_query', }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, + icon: 'circle', + id: 'an1', + key: { + type: 'point_in_time', }, - ], - }, - source: { - layerId: 'first', - groupId: 'xAnnotation', - columnId: 'an1', - id: 'an1', - humanData: { label: 'label' }, - filterOperations: () => true, - }, - target: { - layerId: 'second', - groupId: 'xAnnotation', - columnId: 'an2', - filterOperations: () => true, - }, - dropType: 'swap_compatible', - }).layers - ).toEqual([ - { - layerId: 'first', + label: 'agent.keyword: *', + timeField: 'timestamp', + type: 'query', + }, + ], + ignoreGlobalFilters: true, + }); + }); + it('dragging operation: should copy previous column if passed and assign a new id', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + dropType: 'duplicate_compatible', + source: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'an2', + id: 'an2', + humanData: { label: 'an2' }, + indexPatternId: 'indexPattern1', + }, + target: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'newColId', + filterOperations: Boolean, + indexPatternId: 'indexPattern1', + }, + }).layers[0] + ).toEqual({ + layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2], + annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], ignoreGlobalFilters: true, - }, - { - layerId: 'second', + }); + }); + it('dragging operation: should reorder a dimension to a annotation layer', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation, exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + source: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'an2', + id: 'an2', + humanData: { label: 'label' }, + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + target: { + layerId: 'annotation', + groupId: 'xAnnotation', + columnId: 'an1', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + dropType: 'reorder', + }).layers[0] + ).toEqual({ + layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], + annotations: [exampleAnnotation2, exampleAnnotation], ignoreGlobalFilters: true, - }, - ]); - }); - it('should replace the target in another annotation layer', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, - }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - }, - ], + }); + }); + + it('dragging operation: should duplicate the annotations and replace the target in another annotation layer', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + source: { + layerId: 'first', + groupId: 'xAnnotation', + columnId: 'an1', + id: 'an1', + humanData: { label: 'label' }, + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + target: { + layerId: 'second', + groupId: 'xAnnotation', + columnId: 'an2', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + dropType: 'replace_duplicate_compatible', + }).layers + ).toEqual([ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, }, - source: { + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [{ ...exampleAnnotation, id: 'an2' }], + ignoreGlobalFilters: true, + }, + ]); + }); + it('dragging operation: should swap the annotations between layers', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + source: { + layerId: 'first', + groupId: 'xAnnotation', + columnId: 'an1', + id: 'an1', + humanData: { label: 'label' }, + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + target: { + layerId: 'second', + groupId: 'xAnnotation', + columnId: 'an2', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + dropType: 'swap_compatible', + }).layers + ).toEqual([ + { layerId: 'first', - groupId: 'xAnnotation', - columnId: 'an1', - id: 'an1', - humanData: { label: 'label' }, - filterOperations: () => true, + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, }, - target: { + { layerId: 'second', - groupId: 'xAnnotation', - columnId: 'an2', - filterOperations: () => true, + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, }, - dropType: 'replace_compatible', - }).layers - ).toEqual([ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [], - ignoreGlobalFilters: true, - }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, - }, - ]); - }); - it('should move compatible to another annotation layer', () => { - expect( - xyVisualization.onDrop!({ - frame, - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, - }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [], - ignoreGlobalFilters: true, - }, - ], + ]); + }); + it('dragging operation: should replace the target in another annotation layer', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + }, + ], + }, + source: { + layerId: 'first', + groupId: 'xAnnotation', + columnId: 'an1', + id: 'an1', + humanData: { label: 'label' }, + filterOperations: () => true, + }, + target: { + layerId: 'second', + groupId: 'xAnnotation', + columnId: 'an2', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + dropType: 'replace_compatible', + }).layers + ).toEqual([ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [], + ignoreGlobalFilters: true, }, - source: { + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + ]); + }); + it('dragging operation: should move compatible to another annotation layer', () => { + expect( + xyVisualization.onDrop!({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, + }, + { + layerId: 'second', + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [], + ignoreGlobalFilters: true, + }, + ], + }, + source: { + layerId: 'first', + groupId: 'xAnnotation', + columnId: 'an1', + id: 'an1', + humanData: { label: 'label' }, + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + target: { + layerId: 'second', + groupId: 'xAnnotation', + columnId: 'an2', + filterOperations: () => true, + indexPatternId: 'indexPattern1', + }, + dropType: 'move_compatible', + }).layers + ).toEqual([ + { layerId: 'first', - groupId: 'xAnnotation', - columnId: 'an1', - id: 'an1', - humanData: { label: 'label' }, - filterOperations: () => true, + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [], + ignoreGlobalFilters: true, }, - target: { + { layerId: 'second', - groupId: 'xAnnotation', - columnId: 'an2', - filterOperations: () => true, + layerType: layerTypes.ANNOTATIONS, + indexPatternId: 'indexPattern1', + annotations: [exampleAnnotation], + ignoreGlobalFilters: true, }, - dropType: 'move_compatible', - }).layers - ).toEqual([ - { - layerId: 'first', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [], - ignoreGlobalFilters: true, - }, - { - layerId: 'second', - layerType: layerTypes.ANNOTATIONS, - indexPatternId: 'indexPattern1', - annotations: [exampleAnnotation], - ignoreGlobalFilters: true, - }, - ]); + ]); + }); }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 34ab6c88ffa1..6184e3c607a8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -20,12 +20,17 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { generateId } from '../../id_generator'; -import { renewIDs } from '../../utils'; +import { + isDraggedField, + isOperationFromCompatibleGroup, + isOperationFromTheSameGroup, + renewIDs, +} from '../../utils'; import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader, LayerHeaderContent } from './xy_config_panel/layer_header'; -import type { Visualization, AccessorConfig, FramePublicAPI } from '../../types'; +import { Visualization, AccessorConfig, FramePublicAPI } from '../../types'; import { State, visualizationTypes, @@ -366,6 +371,39 @@ export const getXyVisualization = ({ return getFirstDataLayer(state.layers)?.palette; }, + getDropProps(dropProps) { + if (!dropProps.source) { + return; + } + const srcDataView = dropProps.source.indexPatternId; + const targetDataView = dropProps.target.indexPatternId; + if (!targetDataView || srcDataView !== targetDataView) { + return; + } + + if (isDraggedField(dropProps.source)) { + if (dropProps.source.field.type === 'document') { + return; + } + return dropProps.target.isNewColumn + ? { dropTypes: ['field_add'] } + : { dropTypes: ['field_replace'] }; + } + + if (isOperationFromTheSameGroup(dropProps.source, dropProps.target)) { + return dropProps.target.isNewColumn + ? { dropTypes: ['duplicate_compatible'] } + : { dropTypes: ['reorder'] }; + } + if (isOperationFromCompatibleGroup(dropProps.source, dropProps.target)) { + return { + dropTypes: dropProps.target.isNewColumn + ? ['move_compatible', 'duplicate_compatible'] + : ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'], + }; + } + }, + onDrop(props) { const targetLayer: XYLayerConfig | undefined = props.prevState.layers.find( (l) => l.layerId === props.target.layerId @@ -724,6 +762,9 @@ export const getXyVisualization = ({ getUniqueLabels(state) { return getUniqueLabels(state.layers); }, + getUsedDataView(state, layerId) { + return getAnnotationsLayers(state.layers).find((l) => l.layerId === layerId)?.indexPatternId; + }, getUsedDataViews(state) { return ( state?.layers.filter(isAnnotationsLayer).map(({ indexPatternId }) => indexPatternId) ?? [] From 68bc3811c9a088cfa28c006b4a99884a27944189 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 30 Sep 2022 12:14:08 +0300 Subject: [PATCH 146/185] [Lens][TSVB to Lens] No annotation query string in TSVB and Lens lead to different result (#142197) * [Lens][TSVB to Lens] No annotation query string in TSVB and Lens lead to different result Closes: #142061 * Update layers.ts Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../lib/configurations/xy/layers.test.ts | 71 +++++++++++++++++++ .../lib/configurations/xy/layers.ts | 67 +++++++++-------- 2 files changed, 104 insertions(+), 34 deletions(-) diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts index f869ac64380c..dd4926e7aeb7 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts @@ -229,6 +229,31 @@ describe('getLayers', () => { ], series: [createSeries({ metrics: staticValueMetric })], }); + + const panelWithSingleAnnotationWithoutQueryStringAndTimefield = createPanel({ + annotations: [ + { + fields: 'geo.src,host', + template: 'Security Error from {{geo.src}} on {{host}}', + id: 'ann1', + color: 'rgba(211,49,21,0.7)', + icon: 'fa-asterisk', + ignore_global_filters: 1, + ignore_panel_filters: 1, + time_field: '', + query_string: { + query: '', + language: 'lucene', + }, + hidden: true, + index_pattern: { + id: 'test', + }, + }, + ], + series: [createSeries({ metrics: staticValueMetric })], + }); + const panelWithMultiAnnotations = createPanel({ annotations: [ { @@ -411,6 +436,51 @@ describe('getLayers', () => { }, ], ], + [ + 'annotation layer should gets correct default params', + [dataSourceLayersWithStatic, panelWithSingleAnnotationWithoutQueryStringAndTimefield], + [ + { + layerType: 'referenceLine', + accessors: ['column-id-1'], + layerId: 'test-layer-1', + yConfig: [ + { + forAccessor: 'column-id-1', + axisMode: 'right', + color: '#68BC00', + fill: 'below', + }, + ], + }, + { + layerId: 'test-id', + layerType: 'annotations', + ignoreGlobalFilters: true, + annotations: [ + { + color: '#D33115', + extraFields: ['geo.src'], + filter: { + language: 'lucene', + query: '*', + type: 'kibana_query', + }, + icon: 'asterisk', + id: 'ann1', + isHidden: true, + key: { + type: 'point_in_time', + }, + label: 'Event', + timeField: 'test_field', + type: 'query', + }, + ], + indexPatternId: 'test', + }, + ], + ], [ 'multiple annotations with different data views create separate layers', [dataSourceLayersWithStatic, panelWithMultiAnnotations], @@ -507,6 +577,7 @@ const mockedIndices = [ { id: 'test', title: 'test', + timeFieldName: 'test_field', getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), }, ] as unknown as DataView[]; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts index 6815e278e0f3..7cc2aea19cbd 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts @@ -20,6 +20,7 @@ import Color from 'color'; import { euiLightVars } from '@kbn/ui-theme'; import { groupBy } from 'lodash'; import { DataViewsPublicPluginStart, DataView } from '@kbn/data-plugin/public/data_views'; +import { getDefaultQueryLanguage } from '../../../../application/components/lib/get_default_query_language'; import { fetchIndexPattern } from '../../../../../common/index_patterns_utils'; import { ICON_TYPES_MAP } from '../../../../application/visualizations/constants'; import { SUPPORTED_METRICS } from '../../metrics'; @@ -170,38 +171,36 @@ const convertAnnotation = ( annotation: Annotation, dataView: DataView ): EventAnnotationConfig | undefined => { - if (annotation.query_string) { - const extraFields = annotation.fields - ? annotation.fields - ?.replace(/\s/g, '') - ?.split(',') - .map((field) => { - const dataViewField = dataView.getFieldByName(field); - return dataViewField && dataViewField.aggregatable ? field : undefined; - }) - .filter(nonNullable) - : undefined; - return { - type: 'query', - id: annotation.id, - label: 'Event', - key: { - type: 'point_in_time', - }, - color: new Color(transparentize(annotation.color || euiLightVars.euiColorAccent, 1)).hex(), - timeField: annotation.time_field, - icon: - annotation.icon && - ICON_TYPES_MAP[annotation.icon] && - typeof ICON_TYPES_MAP[annotation.icon] === 'string' - ? ICON_TYPES_MAP[annotation.icon] - : 'triangle', - filter: { - type: 'kibana_query', - ...annotation.query_string, - }, - extraFields, - isHidden: annotation.hidden, - }; - } + const extraFields = annotation.fields + ?.replace(/\s/g, '') + ?.split(',') + .map((field) => { + const dataViewField = dataView.getFieldByName(field); + return dataViewField && dataViewField.aggregatable ? field : undefined; + }) + .filter(nonNullable); + + return { + type: 'query', + id: annotation.id, + label: 'Event', + key: { + type: 'point_in_time', + }, + color: new Color(transparentize(annotation.color || euiLightVars.euiColorAccent, 1)).hex(), + timeField: annotation.time_field || dataView.timeFieldName, + icon: + annotation.icon && + ICON_TYPES_MAP[annotation.icon] && + typeof ICON_TYPES_MAP[annotation.icon] === 'string' + ? ICON_TYPES_MAP[annotation.icon] + : 'triangle', + filter: { + type: 'kibana_query', + query: annotation.query_string?.query || '*', + language: annotation.query_string?.language || getDefaultQueryLanguage(), + }, + extraFields, + isHidden: annotation.hidden, + }; }; From 2d8eb0eed6a8325d7f630cd8f539055e306d371a Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Fri, 30 Sep 2022 11:22:36 +0200 Subject: [PATCH 147/185] [ML] Transforms: Preserves the `field` for unsupported aggs (#142106) * preserve field for unsupported agg * add unit test --- .../__snapshots__/popover_form.test.tsx.snap | 4 +- .../aggregation_list/popover_form.test.tsx | 72 ++++++++++++++++++- .../aggregation_list/popover_form.tsx | 6 +- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/popover_form.test.tsx.snap b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/popover_form.test.tsx.snap index 5f74967ff423..966b3487ebfb 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/popover_form.test.tsx.snap +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/popover_form.test.tsx.snap @@ -2,12 +2,12 @@ exports[`Transform: Aggregation Minimal initialization 1`] = ` ', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + test('Minimal initialization', () => { const defaultData: PivotAggsConfig = { agg: PIVOT_SUPPORTED_AGGS.CARDINALITY, @@ -37,4 +40,67 @@ describe('Transform: Aggregation ', () => { expect(wrapper).toMatchSnapshot(); }); + + test('preserves the field for unsupported aggs', async () => { + const mockOnChange = jest.fn(); + const { getByTestId } = render( + + + + ); + + const aggNameInput = getByTestId('transformAggName'); + fireEvent.change(aggNameInput, { + target: { value: 'betterName' }, + }); + + const applyButton = getByTestId('transformApplyAggChanges'); + fireEvent.click(applyButton); + + expect(mockOnChange).toHaveBeenCalledTimes(1); + expect(mockOnChange).toHaveBeenCalledWith({ + field: 'AvgTicketPrice', + keyed: true, + ranges: [ + { + to: 500, + }, + { + from: 500, + to: 700, + }, + { + from: 700, + }, + ], + // @ts-ignore + agg: 'range', + aggName: 'betterName', + dropDownName: 'AvgTicketPrice.ranges', + }); + }); }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx index 56eeea8a242c..0585d4553a99 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx @@ -100,8 +100,8 @@ export const PopoverForm: React.FC = ({ defaultData, otherAggNames, onCha ...aggConfigDef, agg, aggName, - field: resultField, dropDownName: defaultData.dropDownName, + ...(isUnsupportedAgg ? {} : { field: resultField }), }; return updatedItem; @@ -153,7 +153,7 @@ export const PopoverForm: React.FC = ({ defaultData, otherAggNames, onCha } return ( - + = ({ defaultData, otherAggNames, onCha fontSize="s" language="json" paddingSize="s" - style={{ width: '100%', height: '200px' }} + css={{ width: '100%', height: '200px' }} > {JSON.stringify(getEsAggFromAggConfig(defaultData), null, 2)} From b885d9381de779650cb24d7c8e5a43097adf99d9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 30 Sep 2022 11:37:41 +0200 Subject: [PATCH 148/185] [UnifiedSearch] Add explore matching indices to data view menu (#141807) * add explore matching index * adjust type * move things around * fix types * fix tests * fix imports * fix limit * do not clean datasource on adding ad hoc data view Co-authored-by: Stratoula Kalafateli --- packages/kbn-optimizer/limits.yml | 2 +- .../data_view_editor_flyout_content.tsx | 53 ++++++------- .../components/form_fields/title_field.tsx | 8 +- .../components/form_fields/type_field.tsx | 3 +- .../public/components/form_schema.ts | 2 +- .../indices_list/indices_list.test.tsx | 2 +- .../indices_list/indices_list.tsx | 2 +- .../preview_panel/preview_panel.tsx | 3 +- .../status_message/status_message.test.tsx | 2 +- .../public/lib/get_matched_indices.test.ts | 3 +- .../public/lib/get_matched_indices.ts | 3 +- .../data_view_editor/public/lib/index.ts | 2 - src/plugins/data_view_editor/public/types.ts | 64 ++-------------- .../public/data_views_service_public.ts | 14 +++- src/plugins/data_views/public/index.ts | 9 ++- src/plugins/data_views/public/plugin.ts | 3 +- .../__snapshots__/get_indices.test.ts.snap | 0 .../public/services}/get_indices.test.ts | 0 .../public/services}/get_indices.ts | 10 +-- .../data_views/public/services/index.ts | 13 ++++ src/plugins/data_views/public/types.ts | 62 ++++++++++++++++ .../components/top_nav/discover_topnav.tsx | 16 +++- .../dataview_picker/change_dataview.tsx | 74 ++++++++++++++++++- .../public/dataview_picker/index.tsx | 4 + src/plugins/unified_search/public/types.ts | 1 + src/plugins/unified_search/tsconfig.json | 1 + .../lens/public/app_plugin/lens_top_nav.tsx | 59 ++++++++++++--- .../translations/translations/fr-FR.json | 10 +-- .../translations/translations/ja-JP.json | 10 +-- .../translations/translations/zh-CN.json | 10 +-- 30 files changed, 308 insertions(+), 137 deletions(-) rename src/plugins/{data_view_editor/public/lib => data_views/public/services}/__snapshots__/get_indices.test.ts.snap (100%) rename src/plugins/{data_view_editor/public/lib => data_views/public/services}/get_indices.test.ts (100%) rename src/plugins/{data_view_editor/public/lib => data_views/public/services}/get_indices.ts (90%) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 440ff3ce2b12..5cd145802862 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -23,7 +23,7 @@ pageLoadAssetSize: dataViewEditor: 12000 dataViewFieldEditor: 27000 dataViewManagement: 5000 - dataViews: 44532 + dataViews: 46532 dataVisualizer: 27530 devTools: 38637 discover: 99999 diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index c9baa374ed1d..08a13c7f43d4 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -10,7 +10,12 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import memoizeOne from 'memoize-one'; -import { DataViewField } from '@kbn/data-views-plugin/public'; +import { + DataViewField, + DataViewsPublicPluginStart, + INDEX_PATTERN_TYPE, + MatchedItem, +} from '@kbn/data-views-plugin/public'; import { DataView, @@ -23,16 +28,14 @@ import { UseField, } from '../shared_imports'; -import { ensureMinimumTime, getIndices, extractTimeFields, getMatchedIndices } from '../lib'; +import { ensureMinimumTime, extractTimeFields, getMatchedIndices } from '../lib'; import { FlyoutPanels } from './flyout_panels'; import { removeSpaces } from '../lib'; import { - MatchedItem, DataViewEditorContext, RollupIndicesCapsResponse, - INDEX_PATTERN_TYPE, IndexPatternConfig, MatchedIndicesSet, FormInternal, @@ -176,18 +179,19 @@ const IndexPatternEditorFlyoutContentComponent = ({ // load all data sources and set initial matchedIndices const loadSources = useCallback(() => { - getIndices({ - http, - isRollupIndex: () => false, - pattern: '*', - showAllIndices: allowHidden, - }).then((dataSources) => { - setAllSources(dataSources); - const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden); - setMatchedIndices(matchedSet); - setIsLoadingSources(false); - }); - }, [http, allowHidden]); + dataViews + .getIndices({ + isRollupIndex: () => false, + pattern: '*', + showAllIndices: allowHidden, + }) + .then((dataSources) => { + setAllSources(dataSources); + const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden); + setMatchedIndices(matchedSet); + setIsLoadingSources(false); + }); + }, [allowHidden, dataViews]); // loading list of index patterns useEffect(() => { @@ -271,7 +275,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ const { matchedIndicesResult, exactMatched } = !isLoadingSources ? await loadMatchedIndices(query, allowHidden, allSources, { isRollupIndex, - http, + dataViews, }) : { matchedIndicesResult: { @@ -302,7 +306,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ return fetchIndices(newTitle); }, - [http, allowHidden, allSources, type, rollupIndicesCapabilities, isLoadingSources] + [dataViews, allowHidden, allSources, type, rollupIndicesCapabilities, isLoadingSources] ); // If editData exists, loadSources so that MatchedIndices can be loaded for the Timestampfields @@ -453,10 +457,10 @@ const loadMatchedIndices = memoizeOne( allSources: MatchedItem[], { isRollupIndex, - http, + dataViews, }: { isRollupIndex: (index: string) => boolean; - http: DataViewEditorContext['http']; + dataViews: DataViewsPublicPluginStart; } ): Promise<{ matchedIndicesResult: MatchedIndicesSet; @@ -466,8 +470,7 @@ const loadMatchedIndices = memoizeOne( const indexRequests = []; if (query?.endsWith('*')) { - const exactMatchedQuery = getIndices({ - http, + const exactMatchedQuery = dataViews.getIndices({ isRollupIndex, pattern: query, showAllIndices: allowHidden, @@ -476,14 +479,12 @@ const loadMatchedIndices = memoizeOne( // provide default value when not making a request for the partialMatchQuery indexRequests.push(Promise.resolve([])); } else { - const exactMatchQuery = getIndices({ - http, + const exactMatchQuery = dataViews.getIndices({ isRollupIndex, pattern: query, showAllIndices: allowHidden, }); - const partialMatchQuery = getIndices({ - http, + const partialMatchQuery = dataViews.getIndices({ isRollupIndex, pattern: `${query}*`, showAllIndices: allowHidden, diff --git a/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx b/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx index 822de9506500..9a4c209a56eb 100644 --- a/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx +++ b/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx @@ -9,6 +9,7 @@ import React, { ChangeEvent, useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { MatchedItem } from '@kbn/data-views-plugin/public'; import { UseField, getFieldValidityAndErrorMessage, @@ -17,12 +18,7 @@ import { } from '../../shared_imports'; import { canAppendWildcard, removeSpaces } from '../../lib'; import { schema } from '../form_schema'; -import { - MatchedItem, - RollupIndicesCapsResponse, - IndexPatternConfig, - MatchedIndicesSet, -} from '../../types'; +import { RollupIndicesCapsResponse, IndexPatternConfig, MatchedIndicesSet } from '../../types'; interface RefreshMatchedIndicesResult { matchedIndicesResult: MatchedIndicesSet; diff --git a/src/plugins/data_view_editor/public/components/form_fields/type_field.tsx b/src/plugins/data_view_editor/public/components/form_fields/type_field.tsx index 11b46c7ee31f..b11d8ac2e03e 100644 --- a/src/plugins/data_view_editor/public/components/form_fields/type_field.tsx +++ b/src/plugins/data_view_editor/public/components/form_fields/type_field.tsx @@ -20,9 +20,10 @@ import { EuiBadge, } from '@elastic/eui'; +import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public'; import { UseField } from '../../shared_imports'; -import { INDEX_PATTERN_TYPE, IndexPatternConfig } from '../../types'; +import { IndexPatternConfig } from '../../types'; interface TypeFieldProps { onChange: (type: INDEX_PATTERN_TYPE) => void; diff --git a/src/plugins/data_view_editor/public/components/form_schema.ts b/src/plugins/data_view_editor/public/components/form_schema.ts index 98c3a3b5322e..59e195a1f128 100644 --- a/src/plugins/data_view_editor/public/components/form_schema.ts +++ b/src/plugins/data_view_editor/public/components/form_schema.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import { fieldValidators, ValidationFunc } from '../shared_imports'; -import { INDEX_PATTERN_TYPE } from '../types'; export const singleAstriskValidator = ( ...args: Parameters diff --git a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.test.tsx b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.test.tsx index cad9f323f95e..074865006a38 100644 --- a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.test.tsx +++ b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { IndicesList } from '.'; import { shallow } from 'enzyme'; -import { MatchedItem } from '../../../types'; +import { MatchedItem } from '@kbn/data-views-plugin/public'; const indices = [ { name: 'kibana', tags: [] }, diff --git a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.tsx b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.tsx index db3d1dc49145..51405efc5879 100644 --- a/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.tsx +++ b/src/plugins/data_view_editor/public/components/preview_panel/indices_list/indices_list.tsx @@ -27,7 +27,7 @@ import { import { Pager } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { MatchedItem, Tag } from '../../../types'; +import { MatchedItem, Tag } from '@kbn/data-views-plugin/public'; interface IndicesListProps { indices: MatchedItem[]; diff --git a/src/plugins/data_view_editor/public/components/preview_panel/preview_panel.tsx b/src/plugins/data_view_editor/public/components/preview_panel/preview_panel.tsx index 28413debdb2a..26f6b2b1c2a7 100644 --- a/src/plugins/data_view_editor/public/components/preview_panel/preview_panel.tsx +++ b/src/plugins/data_view_editor/public/components/preview_panel/preview_panel.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; +import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public'; import { StatusMessage } from './status_message'; import { IndicesList } from './indices_list'; -import { INDEX_PATTERN_TYPE, MatchedIndicesSet } from '../../types'; +import { MatchedIndicesSet } from '../../types'; interface Props { type: INDEX_PATTERN_TYPE; diff --git a/src/plugins/data_view_editor/public/components/preview_panel/status_message/status_message.test.tsx b/src/plugins/data_view_editor/public/components/preview_panel/status_message/status_message.test.tsx index 858a00f025e9..23f0e1b8a6b0 100644 --- a/src/plugins/data_view_editor/public/components/preview_panel/status_message/status_message.test.tsx +++ b/src/plugins/data_view_editor/public/components/preview_panel/status_message/status_message.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { StatusMessage } from '.'; import { shallow } from 'enzyme'; -import { MatchedItem } from '../../../types'; +import { MatchedItem } from '@kbn/data-views-plugin/public'; const tagsPartial = { tags: [], diff --git a/src/plugins/data_view_editor/public/lib/get_matched_indices.test.ts b/src/plugins/data_view_editor/public/lib/get_matched_indices.test.ts index b1c80791a134..a5f12f7221ab 100644 --- a/src/plugins/data_view_editor/public/lib/get_matched_indices.test.ts +++ b/src/plugins/data_view_editor/public/lib/get_matched_indices.test.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import { MatchedItem } from '@kbn/data-views-plugin/public'; +import { Tag } from '@kbn/data-views-plugin/public/types'; import { getMatchedIndices } from './get_matched_indices'; -import { Tag, MatchedItem } from '../types'; jest.mock('../constants', () => ({ MAX_NUMBER_OF_MATCHING_INDICES: 6, diff --git a/src/plugins/data_view_editor/public/lib/get_matched_indices.ts b/src/plugins/data_view_editor/public/lib/get_matched_indices.ts index 0b659aa5fbc7..e35ba8f1e8fa 100644 --- a/src/plugins/data_view_editor/public/lib/get_matched_indices.ts +++ b/src/plugins/data_view_editor/public/lib/get_matched_indices.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { MatchedItem } from '@kbn/data-views-plugin/public'; import { MAX_NUMBER_OF_MATCHING_INDICES } from '../constants'; function isSystemIndex(index: string): boolean { @@ -50,7 +51,7 @@ function filterSystemIndices(indices: MatchedItem[], isIncludingSystemIndices: b We call this `exact` matches because ES is telling us exactly what it matches */ -import { MatchedItem, MatchedIndicesSet } from '../types'; +import { MatchedIndicesSet } from '../types'; export function getMatchedIndices( unfilteredAllIndices: MatchedItem[], diff --git a/src/plugins/data_view_editor/public/lib/index.ts b/src/plugins/data_view_editor/public/lib/index.ts index 981c9df03527..97a1f08d3504 100644 --- a/src/plugins/data_view_editor/public/lib/index.ts +++ b/src/plugins/data_view_editor/public/lib/index.ts @@ -10,8 +10,6 @@ export { canAppendWildcard } from './can_append_wildcard'; export { ensureMinimumTime } from './ensure_minimum_time'; -export { getIndices } from './get_indices'; - export { getMatchedIndices } from './get_matched_indices'; export { containsIllegalCharacters } from './contains_illegal_characters'; diff --git a/src/plugins/data_view_editor/public/types.ts b/src/plugins/data_view_editor/public/types.ts index 5b177f65b060..4500e522119d 100644 --- a/src/plugins/data_view_editor/public/types.ts +++ b/src/plugins/data_view_editor/public/types.ts @@ -17,7 +17,12 @@ import { import { EuiComboBoxOptionOption } from '@elastic/eui'; -import type { DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { + DataView, + DataViewsPublicPluginStart, + INDEX_PATTERN_TYPE, + MatchedItem, +} from '@kbn/data-views-plugin/public'; import { DataPublicPluginStart, IndexPatternAggRestrictions } from './shared_imports'; export interface DataViewEditorContext { @@ -80,51 +85,6 @@ export interface StartPlugins { export type CloseEditor = () => void; -export interface MatchedItem { - name: string; - tags: Tag[]; - item: { - name: string; - backing_indices?: string[]; - timestamp_field?: string; - indices?: string[]; - aliases?: string[]; - attributes?: ResolveIndexResponseItemIndexAttrs[]; - data_stream?: string; - }; -} - -// for showing index matches -export interface ResolveIndexResponse { - indices?: ResolveIndexResponseItemIndex[]; - aliases?: ResolveIndexResponseItemAlias[]; - data_streams?: ResolveIndexResponseItemDataStream[]; -} - -export interface ResolveIndexResponseItem { - name: string; -} - -export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem { - backing_indices: string[]; - timestamp_field: string; -} - -export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem { - indices: string[]; -} - -export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem { - aliases?: string[]; - attributes?: ResolveIndexResponseItemIndexAttrs[]; - data_stream?: string; -} - -export interface Tag { - name: string; - key: string; - color: string; -} // end for index matches export interface IndexPatternTableItem { @@ -135,13 +95,6 @@ export interface IndexPatternTableItem { sort: string; } -export enum ResolveIndexResponseItemIndexAttrs { - OPEN = 'open', - CLOSED = 'closed', - HIDDEN = 'hidden', - FROZEN = 'frozen', -} - export interface RollupIndiciesCapability { aggs: Record; error: string; @@ -149,11 +102,6 @@ export interface RollupIndiciesCapability { export type RollupIndicesCapsResponse = Record; -export enum INDEX_PATTERN_TYPE { - ROLLUP = 'rollup', - DEFAULT = 'default', -} - export interface IndexPatternConfig { title: string; timestampField?: EuiComboBoxOptionOption; diff --git a/src/plugins/data_views/public/data_views_service_public.ts b/src/plugins/data_views/public/data_views_service_public.ts index 4693e7000b2a..30625b1b59da 100644 --- a/src/plugins/data_views/public/data_views_service_public.ts +++ b/src/plugins/data_views/public/data_views_service_public.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { DataViewsService } from '.'; +import { DataViewsService, MatchedItem } from '.'; import { DataViewsServiceDeps } from '../common/data_views/data_views'; import { HasDataService } from '../common'; @@ -24,6 +24,11 @@ export interface DataViewsServicePublicDeps extends DataViewsServiceDeps { * Has data service */ hasData: HasDataService; + getIndices: (props: { + pattern: string; + showAllIndices?: boolean; + isRollupIndex: (indexName: string) => boolean; + }) => Promise; } /** @@ -32,6 +37,12 @@ export interface DataViewsServicePublicDeps extends DataViewsServiceDeps { */ export class DataViewsServicePublic extends DataViewsService { public getCanSaveSync: () => boolean; + + public getIndices: (props: { + pattern: string; + showAllIndices?: boolean; + isRollupIndex: (indexName: string) => boolean; + }) => Promise; public hasData: HasDataService; /** @@ -43,5 +54,6 @@ export class DataViewsServicePublic extends DataViewsService { super(deps); this.getCanSaveSync = deps.getCanSaveSync; this.hasData = deps.hasData; + this.getIndices = deps.getIndices; } } diff --git a/src/plugins/data_views/public/index.ts b/src/plugins/data_views/public/index.ts index f886d60696b8..8ee149622f39 100644 --- a/src/plugins/data_views/public/index.ts +++ b/src/plugins/data_views/public/index.ts @@ -32,7 +32,14 @@ export { getFieldSubtypeNested, } from '../common'; -export type { DataViewsPublicSetupDependencies, DataViewsPublicStartDependencies } from './types'; +export type { + DataViewsPublicSetupDependencies, + DataViewsPublicStartDependencies, + MatchedItem, + Tag, +} from './types'; + +export { INDEX_PATTERN_TYPE } from './types'; export type { DataViewsServicePublic, diff --git a/src/plugins/data_views/public/plugin.ts b/src/plugins/data_views/public/plugin.ts index df379ab18964..415a6c97bc73 100644 --- a/src/plugins/data_views/public/plugin.ts +++ b/src/plugins/data_views/public/plugin.ts @@ -21,7 +21,7 @@ import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper import { UiSettingsPublicToCommon } from './ui_settings_wrapper'; import { DataViewsServicePublic } from './data_views_service_public'; -import { HasData } from './services'; +import { getIndices, HasData } from './services'; import { debounceByKey } from './debounce_by_key'; @@ -76,6 +76,7 @@ export class DataViewsPublicPlugin getCanSaveSync: () => application.capabilities.indexPatterns.save === true, getCanSaveAdvancedSettings: () => Promise.resolve(application.capabilities.advancedSettings.save === true), + getIndices: (props) => getIndices({ ...props, http: core.http }), }); } diff --git a/src/plugins/data_view_editor/public/lib/__snapshots__/get_indices.test.ts.snap b/src/plugins/data_views/public/services/__snapshots__/get_indices.test.ts.snap similarity index 100% rename from src/plugins/data_view_editor/public/lib/__snapshots__/get_indices.test.ts.snap rename to src/plugins/data_views/public/services/__snapshots__/get_indices.test.ts.snap diff --git a/src/plugins/data_view_editor/public/lib/get_indices.test.ts b/src/plugins/data_views/public/services/get_indices.test.ts similarity index 100% rename from src/plugins/data_view_editor/public/lib/get_indices.test.ts rename to src/plugins/data_views/public/services/get_indices.test.ts diff --git a/src/plugins/data_view_editor/public/lib/get_indices.ts b/src/plugins/data_views/public/services/get_indices.ts similarity index 90% rename from src/plugins/data_view_editor/public/lib/get_indices.ts rename to src/plugins/data_views/public/services/get_indices.ts index 2c04d6e38996..fba500436752 100644 --- a/src/plugins/data_view_editor/public/lib/get_indices.ts +++ b/src/plugins/data_views/public/services/get_indices.ts @@ -12,20 +12,20 @@ import { i18n } from '@kbn/i18n'; import { Tag, INDEX_PATTERN_TYPE } from '../types'; import { MatchedItem, ResolveIndexResponse, ResolveIndexResponseItemIndexAttrs } from '../types'; -const aliasLabel = i18n.translate('indexPatternEditor.aliasLabel', { defaultMessage: 'Alias' }); -const dataStreamLabel = i18n.translate('indexPatternEditor.dataStreamLabel', { +const aliasLabel = i18n.translate('dataViews.aliasLabel', { defaultMessage: 'Alias' }); +const dataStreamLabel = i18n.translate('dataViews.dataStreamLabel', { defaultMessage: 'Data stream', }); -const indexLabel = i18n.translate('indexPatternEditor.indexLabel', { +const indexLabel = i18n.translate('dataViews.indexLabel', { defaultMessage: 'Index', }); -const frozenLabel = i18n.translate('indexPatternEditor.frozenLabel', { +const frozenLabel = i18n.translate('dataViews.frozenLabel', { defaultMessage: 'Frozen', }); -const rollupLabel = i18n.translate('indexPatternEditor.rollupLabel', { +const rollupLabel = i18n.translate('dataViews.rollupLabel', { defaultMessage: 'Rollup', }); diff --git a/src/plugins/data_views/public/services/index.ts b/src/plugins/data_views/public/services/index.ts index 36d35d69bcb5..af3787bd5dbd 100644 --- a/src/plugins/data_views/public/services/index.ts +++ b/src/plugins/data_views/public/services/index.ts @@ -6,4 +6,17 @@ * Side Public License, v 1. */ +import { HttpStart } from '@kbn/core/public'; +import { MatchedItem } from '../types'; + export * from './has_data'; + +export async function getIndices(props: { + http: HttpStart; + pattern: string; + showAllIndices?: boolean; + isRollupIndex: (indexName: string) => boolean; +}): Promise { + const { getIndices: getIndicesLazy } = await import('./get_indices'); + return getIndicesLazy(props); +} diff --git a/src/plugins/data_views/public/types.ts b/src/plugins/data_views/public/types.ts index fc888d2c42c8..637d4191c671 100644 --- a/src/plugins/data_views/public/types.ts +++ b/src/plugins/data_views/public/types.ts @@ -11,6 +11,11 @@ import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/ import { DataViewsServicePublicMethods } from './data_views'; import { HasDataService } from '../common'; +export enum INDEX_PATTERN_TYPE { + ROLLUP = 'rollup', + DEFAULT = 'default', +} + export enum IndicesResponseItemIndexAttrs { OPEN = 'open', CLOSED = 'closed', @@ -98,6 +103,11 @@ export interface DataViewsPublicPluginSetup {} export interface DataViewsServicePublic extends DataViewsServicePublicMethods { getCanSaveSync: () => boolean; hasData: HasDataService; + getIndices: (props: { + pattern: string; + showAllIndices?: boolean; + isRollupIndex: (indexName: string) => boolean; + }) => Promise; } export type DataViewsContract = DataViewsServicePublic; @@ -106,3 +116,55 @@ export type DataViewsContract = DataViewsServicePublic; * Data views plugin public Start contract */ export type DataViewsPublicPluginStart = DataViewsServicePublic; + +export interface MatchedItem { + name: string; + tags: Tag[]; + item: { + name: string; + backing_indices?: string[]; + timestamp_field?: string; + indices?: string[]; + aliases?: string[]; + attributes?: ResolveIndexResponseItemIndexAttrs[]; + data_stream?: string; + }; +} + +// for showing index matches +export interface ResolveIndexResponse { + indices?: ResolveIndexResponseItemIndex[]; + aliases?: ResolveIndexResponseItemAlias[]; + data_streams?: ResolveIndexResponseItemDataStream[]; +} + +export interface ResolveIndexResponseItem { + name: string; +} + +export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem { + backing_indices: string[]; + timestamp_field: string; +} + +export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem { + indices: string[]; +} + +export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem { + aliases?: string[]; + attributes?: ResolveIndexResponseItemIndexAttrs[]; + data_stream?: string; +} + +export interface Tag { + name: string; + key: string; + color: string; +} +export enum ResolveIndexResponseItemIndexAttrs { + OPEN = 'open', + CLOSED = 'closed', + HIDDEN = 'hidden', + FROZEN = 'frozen', +} diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 241a20a0a155..82f3b1aadadb 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -66,7 +66,7 @@ export const DiscoverTopNav = ({ [dataView] ); const services = useDiscoverServices(); - const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings } = services; + const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, dataViews } = services; const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); @@ -141,6 +141,19 @@ export const DiscoverTopNav = ({ [canEditDataView, dataViewEditor, onChangeDataView] ); + const onCreateDefaultAdHocDataView = useCallback( + async (pattern: string) => { + const newDataView = await dataViews.create({ + title: pattern, + }); + if (newDataView.fields.getByName('@timestamp')?.type === 'date') { + newDataView.timeFieldName = '@timestamp'; + } + onChangeDataView(newDataView.id!); + }, + [dataViews, onChangeDataView] + ); + const topNavMenu = useMemo( () => getTopNavLinks({ @@ -201,6 +214,7 @@ export const DiscoverTopNav = ({ currentDataViewId: dataView?.id, onAddField: addField, onDataViewCreated: createNewDataView, + onCreateDefaultAdHocDataView, onChangeDataView, textBasedLanguages: supportedTextBasedLanguages as DataViewPickerProps['textBasedLanguages'], adHocDataViews: adHocDataViewList, diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 2f641cd2d4e2..72a2d8fea290 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; import { css } from '@emotion/react'; import { EuiPopover, @@ -24,6 +24,7 @@ import { EuiFlexItem, EuiButtonEmpty, EuiToolTip, + EuiSpacer, } from '@elastic/eui'; import type { DataViewListItem } from '@kbn/data-views-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; @@ -71,9 +72,13 @@ export function ChangeDataView({ onTextLangQuerySubmit, textBasedLanguage, isDisabled, + onCreateDefaultAdHocDataView, }: DataViewPickerPropsExtended) { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const [noDataViewMatches, setNoDataViewMatches] = useState(false); + const [dataViewSearchString, setDataViewSearchString] = useState(''); + const [indexMatches, setIndexMatches] = useState(0); const [dataViewsList, setDataViewsList] = useState([]); const [triggerLabel, setTriggerLabel] = useState(''); const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState( @@ -111,6 +116,24 @@ export function ChangeDataView({ fetchDataViews(); }, [data, currentDataViewId, adHocDataViews]); + const pendingIndexMatch = useRef(); + useEffect(() => { + async function checkIndices() { + if (dataViewSearchString !== '' && noDataViewMatches) { + const matches = await kibana.services.dataViews.getIndices({ + pattern: dataViewSearchString, + isRollupIndex: () => false, + showAllIndices: false, + }); + setIndexMatches(matches.length); + } + } + if (pendingIndexMatch.current) { + clearTimeout(pendingIndexMatch.current); + } + pendingIndexMatch.current = setTimeout(checkIndices, 250); + }, [dataViewSearchString, kibana.services.dataViews, noDataViewMatches]); + useEffect(() => { if (trigger.label) { if (textBasedLanguage) { @@ -282,10 +305,57 @@ export function ChangeDataView({ } }} currentDataViewId={currentDataViewId} - selectableProps={selectableProps} + selectableProps={{ + ...(selectableProps || {}), + // @ts-expect-error Some EUI weirdness + searchProps: { + ...(selectableProps?.searchProps || {}), + onChange: (value, matches) => { + selectableProps?.searchProps?.onChange?.(value, matches); + setNoDataViewMatches(matches.length === 0 && dataViewsList.length > 0); + setDataViewSearchString(value); + }, + }, + }} searchListInputId={searchListInputId} isTextBasedLangSelected={isTextBasedLangSelected} /> + {onCreateDefaultAdHocDataView && noDataViewMatches && indexMatches > 0 && ( + + + { + setPopoverIsOpen(false); + onCreateDefaultAdHocDataView(dataViewSearchString); + }} + > + {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.createForMatchingIndices', + { + defaultMessage: `Explore {indicesLength, plural, + one {# matching index} + other {# matching indices}}`, + values: { + indicesLength: indexMatches, + }, + } + )} + + + + + )} ); diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 1cc0a8219af9..9fb794d58e64 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -63,6 +63,8 @@ export interface DataViewPickerProps { * Also works as a flag to show the create dataview button. */ onDataViewCreated?: () => void; + + onCreateDefaultAdHocDataView?: (pattern: string) => void; /** * List of the supported text based languages (SQL, ESQL) etc. * Defined per application, if not provided, no text based languages @@ -104,6 +106,7 @@ export const DataViewPicker = ({ onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, + onCreateDefaultAdHocDataView, isDisabled, }: DataViewPickerPropsExtended) => { return ( @@ -113,6 +116,7 @@ export const DataViewPicker = ({ onChangeDataView={onChangeDataView} onAddField={onAddField} onDataViewCreated={onDataViewCreated} + onCreateDefaultAdHocDataView={onCreateDefaultAdHocDataView} trigger={trigger} adHocDataViews={adHocDataViews} selectableProps={selectableProps} diff --git a/src/plugins/unified_search/public/types.ts b/src/plugins/unified_search/public/types.ts index d079cd72edf8..85f32a9d4b7e 100755 --- a/src/plugins/unified_search/public/types.ts +++ b/src/plugins/unified_search/public/types.ts @@ -86,5 +86,6 @@ export interface IUnifiedSearchPluginServices extends Partial { storage: IStorageWrapper; docLinks: DocLinksStart; data: DataPublicPluginStart; + dataViews: DataViewsPublicPluginStart; usageCollection?: UsageCollectionStart; } diff --git a/src/plugins/unified_search/tsconfig.json b/src/plugins/unified_search/tsconfig.json index 61b1f8305882..2f09d27c84b0 100644 --- a/src/plugins/unified_search/tsconfig.json +++ b/src/plugins/unified_search/tsconfig.json @@ -17,6 +17,7 @@ { "path": "../../core/tsconfig.json" }, { "path": "../data/tsconfig.json" }, { "path": "../data_views/tsconfig.json" }, + { "path": "../data_view_editor/tsconfig.json" }, { "path": "../embeddable/tsconfig.json" }, { "path": "../usage_collection/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, 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 8875d2d0f983..e72253f6e2a5 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 @@ -15,8 +15,8 @@ import { downloadMultipleAs } from '@kbn/share-plugin/public'; import { tableHasFormulas } from '@kbn/data-plugin/common'; import { exporters, getEsQueryConfig } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import { ENABLE_SQL } from '../../common'; import { LensAppServices, @@ -768,13 +768,15 @@ export const LensTopNavMenu = ({ closeDataViewEditor.current = dataViewEditor.openEditor({ onSave: async (dataView) => { if (dataView.id) { - dispatch( - switchAndCleanDatasource({ - newDatasourceId: 'indexpattern', - visualizationId: visualization?.activeId, - currentIndexPatternId: dataView?.id, - }) - ); + if (isOnTextBasedMode) { + dispatch( + switchAndCleanDatasource({ + newDatasourceId: 'indexpattern', + visualizationId: visualization?.activeId, + currentIndexPatternId: dataView?.id, + }) + ); + } dispatchChangeIndexPattern(dataView); setCurrentIndexPattern(dataView); } @@ -783,7 +785,43 @@ export const LensTopNavMenu = ({ }); } : undefined, - [canEditDataView, dataViewEditor, dispatch, dispatchChangeIndexPattern, visualization?.activeId] + [ + canEditDataView, + dataViewEditor, + dispatch, + dispatchChangeIndexPattern, + isOnTextBasedMode, + visualization?.activeId, + ] + ); + + const onCreateDefaultAdHocDataView = useCallback( + async (pattern: string) => { + const dataView = await dataViewsService.create({ + title: pattern, + }); + if (dataView.fields.getByName('@timestamp')?.type === 'date') { + dataView.timeFieldName = '@timestamp'; + } + if (isOnTextBasedMode) { + dispatch( + switchAndCleanDatasource({ + newDatasourceId: 'indexpattern', + visualizationId: visualization?.activeId, + currentIndexPatternId: dataView?.id, + }) + ); + } + dispatchChangeIndexPattern(dataView); + setCurrentIndexPattern(dataView); + }, + [ + dataViewsService, + dispatch, + dispatchChangeIndexPattern, + isOnTextBasedMode, + visualization?.activeId, + ] ); // setting that enables/disables SQL @@ -793,7 +831,7 @@ export const LensTopNavMenu = ({ supportedTextBasedLanguages.push('SQL'); } - const dataViewPickerProps = { + const dataViewPickerProps: DataViewPickerProps = { trigger: { label: currentIndexPattern?.getName?.() || '', 'data-test-subj': 'lns-dataView-switch-link', @@ -802,6 +840,7 @@ export const LensTopNavMenu = ({ currentDataViewId: currentIndexPattern?.id, onAddField: addField, onDataViewCreated: createNewDataView, + onCreateDefaultAdHocDataView, adHocDataViews: indexPatterns.filter((pattern) => !pattern.isPersisted()), onChangeDataView: (newIndexPatternId: string) => { const currentDataView = dataViewsList.find( diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 09d5ebabd0d0..f9648f0fa6d3 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -3720,10 +3720,10 @@ "indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "Votre modèle d'indexation ne correspond à aucun flux de données, index ni alias d'index, mais {strongIndices} {matchedIndicesLength, plural, one {est semblable} other {sont semblables} }.", "indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, one {source} other {# sources} }", "indexPatternEditor.status.successLabel.successDetail": "Votre modèle d'indexation correspond à {sourceCount} {sourceCount, plural, one {source} other {sources} }.", - "indexPatternEditor.aliasLabel": "Alias", + "dataViews.aliasLabel": "Alias", "indexPatternEditor.createIndex.noMatch": "Le nom doit correspondre à au moins un flux de données, index ou alias d'index.", "indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- Je ne souhaite pas utiliser le filtre temporel ---", - "indexPatternEditor.dataStreamLabel": "Flux de données", + "dataViews.dataStreamLabel": "Flux de données", "indexPatternEditor.dataView.unableSaveLabel": "Échec de l'enregistrement de la vue de données.", "indexPatternEditor.dataViewExists.ValidationErrorMessage": "Une vue de données de ce nom existe déjà.", "indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "Confirmer", @@ -3754,8 +3754,8 @@ "indexPatternEditor.form.customIndexPatternIdLabel": "ID de vue de données personnalisé", "indexPatternEditor.form.nameAriaLabel": "Champ de nom facultatif", "indexPatternEditor.form.titleAriaLabel": "Champ de modèle d'indexation", - "indexPatternEditor.frozenLabel": "Frozen", - "indexPatternEditor.indexLabel": "Index", + "dataViews.frozenLabel": "Frozen", + "dataViews.indexLabel": "Index", "indexPatternEditor.loadingHeader": "Recherche d'index correspondants…", "indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "Sélectionnez un champ d'horodatage.", "indexPatternEditor.rollupDataView.createIndex.noMatchError": "Erreur de vue de données de cumul : doit correspondre à un index de cumul", @@ -3763,7 +3763,7 @@ "indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibana propose un support bêta pour les vues de données basées sur les cumuls. Vous pourriez rencontrer des problèmes lors de l'utilisation de ces vues dans les recherches enregistrées, les visualisations et les tableaux de bord. Ils ne sont pas compatibles avec certaines fonctionnalités avancées, telles que Timelion et le Machine Learning.", "indexPatternEditor.rollupDataView.warning.textParagraphTwo": "Vous pouvez mettre une vue de données de cumul en correspondance avec un index de cumul et zéro index régulier ou plus. Une vue de données de cumul dispose d'indicateurs, de champs, d'intervalles et d'agrégations limités. Un index de cumul se limite aux index disposant d'une configuration de tâche ou de plusieurs tâches avec des configurations compatibles.", "indexPatternEditor.rollupIndexPattern.warning.title": "Fonctionnalité bêta", - "indexPatternEditor.rollupLabel": "Cumul", + "dataViews.rollupLabel": "Cumul", "indexPatternEditor.saved": "'{indexPatternName}' enregistré", "indexPatternEditor.status.noSystemIndicesLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.", "indexPatternEditor.status.noSystemIndicesWithPromptLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 32c2dbbf5f01..db62f093fbda 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3715,10 +3715,10 @@ "indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "インデックスパターンはどのデータストリーム、インデックス、インデックスエイリアスとも一致しませんが、{strongIndices} {matchedIndicesLength, plural, other {が} }類似しています。", "indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, other {# ソース} }", "indexPatternEditor.status.successLabel.successDetail": "インデックスパターンは、{sourceCount} {sourceCount, plural, other {ソース} }と一致します。", - "indexPatternEditor.aliasLabel": "エイリアス", + "dataViews.aliasLabel": "エイリアス", "indexPatternEditor.createIndex.noMatch": "名前は1つ以上のデータストリーム、インデックス、またはインデックスエイリアスと一致する必要があります。", "indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- 時間フィルターを使用しない ---", - "indexPatternEditor.dataStreamLabel": "データストリーム", + "dataViews.dataStreamLabel": "データストリーム", "indexPatternEditor.dataView.unableSaveLabel": "データビューの保存に失敗しました。", "indexPatternEditor.dataViewExists.ValidationErrorMessage": "この名前のデータビューはすでに存在します。", "indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "確認", @@ -3749,8 +3749,8 @@ "indexPatternEditor.form.customIndexPatternIdLabel": "カスタムデータビューID", "indexPatternEditor.form.nameAriaLabel": "名前フィールド(任意)", "indexPatternEditor.form.titleAriaLabel": "インデックスパターンフィールド", - "indexPatternEditor.frozenLabel": "凍結", - "indexPatternEditor.indexLabel": "インデックス", + "dataViews.frozenLabel": "凍結", + "dataViews.indexLabel": "インデックス", "indexPatternEditor.loadingHeader": "一致するインデックスを検索中…", "indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "タイムスタンプフィールドを選択します。", "indexPatternEditor.rollupDataView.createIndex.noMatchError": "ロールアップデータビューエラー:ロールアップインデックスの 1 つと一致している必要があります", @@ -3758,7 +3758,7 @@ "indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibanaではロールアップに基づいてデータビューのデータサポートを提供します。保存された検索、可視化、ダッシュボードでこれらを使用すると問題が発生する場合があります。Timelionや機械学習などの一部の高度な機能ではサポートされていません。", "indexPatternEditor.rollupDataView.warning.textParagraphTwo": "ロールアップデータビューは、1つのロールアップインデックスとゼロ以上の標準インデックスと一致させることができます。ロールアップデータビューでは、メトリック、フィールド、間隔、アグリゲーションが制限されています。ロールアップインデックスは、1つのジョブ構成があるインデックス、または複数のジョブと互換する構成があるインデックスに制限されています。", "indexPatternEditor.rollupIndexPattern.warning.title": "ベータ機能", - "indexPatternEditor.rollupLabel": "ロールアップ", + "dataViews.rollupLabel": "ロールアップ", "indexPatternEditor.saved": "'{indexPatternName}'が保存されました", "indexPatternEditor.status.noSystemIndicesLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。", "indexPatternEditor.status.noSystemIndicesWithPromptLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 48fe03474e19..5f5d2638f611 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3720,10 +3720,10 @@ "indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "您的索引模式不匹配任何数据流、索引或索引别名,但{strongIndices}{matchedIndicesLength, plural, other {} }类似。", "indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, one {个源} other {# 个源} }", "indexPatternEditor.status.successLabel.successDetail": "您的索引模式匹配 {sourceCount} 个{sourceCount, plural, other {源} }。", - "indexPatternEditor.aliasLabel": "别名", + "dataViews.aliasLabel": "别名", "indexPatternEditor.createIndex.noMatch": "名称必须匹配一个或多个数据流、索引或索引别名。", "indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- 我不想使用时间筛选 ---", - "indexPatternEditor.dataStreamLabel": "数据流", + "dataViews.dataStreamLabel": "数据流", "indexPatternEditor.dataView.unableSaveLabel": "无法保存数据视图。", "indexPatternEditor.dataViewExists.ValidationErrorMessage": "具有此名称的数据视图已存在。", "indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "确认", @@ -3754,8 +3754,8 @@ "indexPatternEditor.form.customIndexPatternIdLabel": "定制数据视图 ID", "indexPatternEditor.form.nameAriaLabel": "名称字段(可选)", "indexPatternEditor.form.titleAriaLabel": "索引模式字段", - "indexPatternEditor.frozenLabel": "已冻结", - "indexPatternEditor.indexLabel": "索引", + "dataViews.frozenLabel": "已冻结", + "dataViews.indexLabel": "索引", "indexPatternEditor.loadingHeader": "正在寻找匹配的索引......", "indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "选择时间戳字段。", "indexPatternEditor.rollupDataView.createIndex.noMatchError": "汇总/打包数据视图错误:必须匹配一个汇总/打包索引", @@ -3763,7 +3763,7 @@ "indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibana 基于汇总/打包为数据视图提供公测版支持。将这些视图用于已保存搜索、可视化以及仪表板可能会遇到问题。某些高级功能,如 Timelion 和 Machine Learning,不支持这些模式。", "indexPatternEditor.rollupDataView.warning.textParagraphTwo": "可以根据一个汇总/打包索引和零个或更多常规索引匹配汇总/打包数据视图。汇总/打包数据视图的指标、字段、时间间隔和聚合有限。汇总/打包索引仅限于具有一个作业配置或多个作业配置兼容的索引。", "indexPatternEditor.rollupIndexPattern.warning.title": "公测版功能", - "indexPatternEditor.rollupLabel": "汇总/打包", + "dataViews.rollupLabel": "汇总/打包", "indexPatternEditor.saved": "已保存“{indexPatternName}”", "indexPatternEditor.status.noSystemIndicesLabel": "没有数据流、索引或索引别名匹配您的索引模式。", "indexPatternEditor.status.noSystemIndicesWithPromptLabel": "没有数据流、索引或索引别名匹配您的索引模式。", From 802039ec34ba2129041a9515b79c70a120839491 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 30 Sep 2022 12:48:31 +0300 Subject: [PATCH 149/185] [Lens][Discover] Visualize text based query (#141928) * [Lens] Enable text-based languages-sql * Display data * Chart switcher and further fixes * Drag and drop fixes * Small fix * Multiple improvements * Errors implementation and save and exit * Some cleanup * Fix types failures * Revert change * Fix underlying data error * Fix jest test * Fixes app test * Rename datasource to textBased * display the dataview picker component but disabled * Fix functional test * Refactoring * Populate the dataview to theembeddable * Load sync * sync load of the new dtsource * Fix * Fix bug when the dtaview is not found * Refactoring * Add some unit tests * Add some unit tests * Add more unit tests * Add a functional test * Add all files * Update lens limit * Fixes bug * Bump lens size * Fix flakiness * Further fixes * Fix check * More fixes * Fix test * Wait for query to run * More changes * Fix * Fix the function input to fetch from variable * Initial implementation of visualizing fields * Text based languages Visualization in Lens * Fix due to bad conflict * Fix types * Revert from main * Fix jest test * Add unit tests * Adds a functional test * Visualize lens field * Add a unit test * Move switch datasource logic to loadInitial * Cleanup --- .../sidebar/discover_sidebar.test.tsx | 10 ++ .../components/sidebar/discover_sidebar.tsx | 22 +++++ .../sidebar/lib/visualize_trigger_utils.ts | 17 ++++ src/plugins/ui_actions/public/types.ts | 2 + .../editor_frame/suggestion_helpers.ts | 6 ++ .../init_middleware/load_initial.ts | 6 ++ .../text_based_languages.test.ts | 92 +++++++++++++++++++ .../text_based_languages.tsx | 84 ++++++++++++++++- .../text_based_languages_datasource/types.ts | 3 + .../utils.test.ts | 22 +++++ .../text_based_languages_datasource/utils.ts | 13 ++- .../apps/discover/visualize_field.ts | 26 ++++++ 12 files changed, 294 insertions(+), 9 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx index 22d33215b1ea..666b75a68949 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx @@ -194,4 +194,14 @@ describe('discover sidebar', function () { const createDataViewButton = findTestSubject(compWithPickerInViewerMode, 'dataview-create-new'); expect(createDataViewButton.length).toBe(0); }); + + it('should render the Visualize in Lens button in text based languages mode', () => { + const compInViewerMode = mountWithIntl( + + + + ); + const visualizeField = findTestSubject(compInViewerMode, 'textBased-visualize'); + expect(visualizeField.length).toBe(1); + }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index d7e227df94b8..355512da5c52 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -22,6 +22,7 @@ import { useResizeObserver, EuiButton, } from '@elastic/eui'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import useShallowCompareEffect from 'react-use/lib/useShallowCompareEffect'; import { isEqual } from 'lodash'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -39,6 +40,7 @@ import { DiscoverSidebarResponsiveProps } from './discover_sidebar_responsive'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { DISCOVER_TOUR_STEP_ANCHOR_IDS } from '../../../../components/discover_tour'; import type { DataTableRecord } from '../../../../types'; +import { triggerVisualizeActionsTextBasedLanguages } from './lib/visualize_trigger_utils'; /** * Default number of available fields displayed and added on scroll @@ -309,6 +311,12 @@ export function DiscoverSidebarComponent({ const filterChanged = useMemo(() => isEqual(fieldFilter, getDefaultFieldFilter()), [fieldFilter]); + const visualizeAggregateQuery = useCallback(() => { + const aggregateQuery = + state.query && isOfAggregateQueryType(state.query) ? state.query : undefined; + triggerVisualizeActionsTextBasedLanguages(columns, selectedDataView, aggregateQuery); + }, [columns, selectedDataView, state.query]); + if (!selectedDataView) { return null; } @@ -532,6 +540,20 @@ export function DiscoverSidebarComponent({ )} + {isPlainRecord && ( + + + {i18n.translate('discover.textBasedLanguages.visualize.label', { + defaultMessage: 'Visualize in Lens', + })} + + + )} ); diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts index 005accf3021f..85537df2ef36 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/visualize_trigger_utils.ts @@ -12,6 +12,7 @@ import { visualizeFieldTrigger, visualizeGeoFieldTrigger, } from '@kbn/ui-actions-plugin/public'; +import type { AggregateQuery } from '@kbn/es-query'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; import { KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; import { getUiActions } from '../../../../../kibana_services'; @@ -59,6 +60,22 @@ export function triggerVisualizeActions( getUiActions().getTrigger(trigger).exec(triggerOptions); } +export function triggerVisualizeActionsTextBasedLanguages( + contextualFields: string[], + dataView?: DataView, + query?: AggregateQuery +) { + if (!dataView) return; + const triggerOptions = { + dataViewSpec: dataView.toSpec(false), + fieldName: '', + contextualFields, + originatingApp: PLUGIN_ID, + query, + }; + getUiActions().getTrigger(VISUALIZE_FIELD_TRIGGER).exec(triggerOptions); +} + export interface VisualizeInformation { field: DataViewField; href?: string; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index a76a8aa760ed..fb2d9869e21c 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { AggregateQuery } from '@kbn/es-query'; import type { DataViewSpec } from '@kbn/data-views-plugin/public'; import { ActionInternal } from './actions/action_internal'; import { TriggerInternal } from './triggers/trigger_internal'; @@ -19,6 +20,7 @@ export interface VisualizeFieldContext { dataViewSpec: DataViewSpec; contextualFields?: string[]; originatingApp?: string; + query?: AggregateQuery; } export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index ec8eb224678b..21cab9188999 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -205,6 +205,12 @@ export function getVisualizeFieldSuggestions({ context: visualizeTriggerFieldContext, }); } + // suggestions for visualizing textbased languages + if (visualizeTriggerFieldContext && 'query' in visualizeTriggerFieldContext) { + if (visualizeTriggerFieldContext.query) { + return suggestions.find((s) => s.datasourceId === 'textBasedLanguages'); + } + } if (suggestions.length) { return suggestions.find((s) => s.visualizationId === activeVisualization?.id) || suggestions[0]; diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index dc2425917bad..5b942eff4a68 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -115,6 +115,11 @@ export function loadInitial( defaultIndexPatternId: lensServices.uiSettings.get('defaultIndex'), }; + let activeDatasourceId: string | undefined; + if (initialContext && 'query' in initialContext) { + activeDatasourceId = 'textBasedLanguages'; + } + if ( !initialInput || (attributeService.inputIsRefType(initialInput) && @@ -141,6 +146,7 @@ export function loadInitial( ...emptyState, dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), searchSessionId: data.search.session.getSessionId() || data.search.session.start(), + ...(activeDatasourceId && { activeDatasourceId }), datasourceStates: Object.entries(datasourceStates).reduce( (state, [datasourceId, datasourceState]) => ({ ...state, diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts index c2238bfd9fef..92a3ef01fdb7 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.test.ts @@ -12,6 +12,7 @@ import { TextBasedLanguagesPersistedState, TextBasedLanguagesPrivateState } from import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { getTextBasedLanguagesDatasource } from './text_based_languages'; +import { generateId } from '../id_generator'; import { DatasourcePublicAPI, Datasource } from '../types'; jest.mock('../id_generator'); @@ -272,6 +273,97 @@ describe('IndexPattern Data Source', () => { }); }); + describe('#getDatasourceSuggestionsForVisualizeField', () => { + (generateId as jest.Mock).mockReturnValue(`newid`); + it('should create the correct layers', () => { + const state = { + layers: {}, + initialContext: { + contextualFields: ['bytes', 'dest'], + query: { sql: 'SELECT * FROM "foo"' }, + dataViewSpec: { + title: 'foo', + id: '1', + name: 'Foo', + }, + }, + } as unknown as TextBasedLanguagesPrivateState; + const suggestions = textBasedLanguagesDatasource.getDatasourceSuggestionsForVisualizeField( + state, + '1', + '', + indexPatterns + ); + expect(suggestions[0].state).toEqual({ + ...state, + layers: { + newid: { + allColumns: [ + { + columnId: 'newid', + fieldName: 'bytes', + meta: { + type: 'number', + }, + }, + { + columnId: 'newid', + fieldName: 'dest', + meta: { + type: 'string', + }, + }, + ], + columns: [ + { + columnId: 'newid', + fieldName: 'bytes', + meta: { + type: 'number', + }, + }, + { + columnId: 'newid', + fieldName: 'dest', + meta: { + type: 'string', + }, + }, + ], + index: 'foo', + query: { + sql: 'SELECT * FROM "foo"', + }, + }, + }, + }); + + expect(suggestions[0].table).toEqual({ + changeType: 'initial', + columns: [ + { + columnId: 'newid', + operation: { + dataType: 'number', + isBucketed: false, + label: 'bytes', + }, + }, + { + columnId: 'newid', + operation: { + dataType: 'string', + isBucketed: true, + label: 'dest', + }, + }, + ], + isMultiRow: false, + layerId: 'newid', + }); + }); + }); + describe('#getErrorMessages', () => { it('should use the results of getErrorMessages directly when single layer', () => { const state = { diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx index 4857d3eed3e3..e9ab24b8392c 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/text_based_languages.tsx @@ -14,7 +14,7 @@ import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { SavedObjectReference } from '@kbn/core/public'; import { EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; -import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; +import type { ExpressionsStart, DatatableColumnType } from '@kbn/expressions-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { @@ -36,7 +36,7 @@ import type { TextBasedLanguageField, } from './types'; import { FieldSelect } from './field_select'; -import { Datasource } from '../types'; +import type { Datasource, IndexPatternMap } from '../types'; import { LayerPanel } from './layerpanel'; function getLayerReferenceName(layerId: string) { @@ -82,6 +82,77 @@ export function getTextBasedLanguagesDatasource({ }; }); }; + const getSuggestionsForVisualizeField = ( + state: TextBasedLanguagesPrivateState, + indexPatternId: string, + fieldName: string, + indexPatterns: IndexPatternMap + ) => { + const context = state.initialContext; + if (context && 'dataViewSpec' in context && context.dataViewSpec.title) { + const newLayerId = generateId(); + const indexPattern = indexPatterns[indexPatternId]; + + const contextualFields = context.contextualFields; + const newColumns = contextualFields?.map((c) => { + let field = indexPattern?.getFieldByName(c); + if (!field) { + field = indexPattern?.fields.find((f) => f.name.includes(c)); + } + const newId = generateId(); + const type = field?.type ?? 'number'; + return { + columnId: newId, + fieldName: c, + meta: { + type: type as DatatableColumnType, + }, + }; + }); + + const index = context.dataViewSpec.title; + const query = context.query; + const updatedState = { + ...state, + layers: { + ...state.layers, + [newLayerId]: { + index, + query, + columns: newColumns ?? [], + allColumns: newColumns ?? [], + }, + }, + }; + + return [ + { + state: { + ...updatedState, + }, + table: { + changeType: 'initial' as TableChangeType, + isMultiRow: false, + layerId: newLayerId, + columns: + newColumns?.map((f) => { + return { + columnId: f.columnId, + operation: { + dataType: f?.meta?.type as DataType, + label: f.fieldName, + isBucketed: Boolean(f?.meta?.type !== 'number'), + }, + }; + }) ?? [], + }, + keptLayerIds: [newLayerId], + }, + ]; + } + + return []; + }; const TextBasedLanguagesDatasource: Datasource< TextBasedLanguagesPrivateState, TextBasedLanguagesPersistedState @@ -137,6 +208,7 @@ export function getTextBasedLanguagesDatasource({ ...initState, fieldList: [], indexPatternRefs: refs, + initialContext: context, }; }, onRefreshIndexPattern() {}, @@ -291,7 +363,11 @@ export function getTextBasedLanguagesDatasource({ const columnExists = props.state.fieldList.some((f) => f.name === selectedField?.fieldName); render( - {}}> + {}} + data-test-subj="lns-dimensionTrigger-textBased" + > {customLabel ?? i18n.translate('xpack.lens.textBasedLanguages.missingField', { defaultMessage: 'Missing field', @@ -564,7 +640,7 @@ export function getTextBasedLanguagesDatasource({ }); return []; }, - getDatasourceSuggestionsForVisualizeField: getSuggestionsForState, + getDatasourceSuggestionsForVisualizeField: getSuggestionsForVisualizeField, getDatasourceSuggestionsFromCurrentState: getSuggestionsForState, getDatasourceSuggestionsForVisualizeCharts: getSuggestionsForState, isEqual: () => true, diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/types.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/types.ts index 2ea1692cf37c..11b9612624ef 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/types.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/types.ts @@ -6,6 +6,8 @@ */ import type { DatatableColumn } from '@kbn/expressions-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; +import type { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import type { VisualizeEditorContext } from '../types'; export interface TextBasedLanguagesLayerColumn { columnId: string; @@ -34,6 +36,7 @@ export interface TextBasedLanguagesPersistedState { export type TextBasedLanguagesPrivateState = TextBasedLanguagesPersistedState & { indexPatternRefs: IndexPatternRef[]; fieldList: DatatableColumn[]; + initialContext?: VisualizeFieldContext | VisualizeEditorContext; }; export interface IndexPatternRef { diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts index 1cbd830a92fc..bc511bb2b86c 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.test.ts @@ -84,6 +84,18 @@ describe('Text based languages utils', () => { index: '', }, }, + indexPatternRefs: [], + fieldList: [], + initialContext: { + contextualFields: ['bytes', 'dest'], + query: { sql: 'SELECT * FROM "foo"' }, + fieldName: '', + dataViewSpec: { + title: 'foo', + id: '1', + name: 'Foo', + }, + }, }; const dataViewsMock = dataViewPluginMocks.createStartContract(); const dataMock = dataPluginMock.createStartContract(); @@ -113,6 +125,16 @@ describe('Text based languages utils', () => { ); expect(updatedState).toStrictEqual({ + initialContext: { + contextualFields: ['bytes', 'dest'], + query: { sql: 'SELECT * FROM "foo"' }, + fieldName: '', + dataViewSpec: { + title: 'foo', + id: '1', + name: 'Foo', + }, + }, fieldList: [ { name: 'timestamp', diff --git a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts index 6c17d5206efb..c4e41103f0fd 100644 --- a/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts +++ b/x-pack/plugins/lens/public/text_based_languages_datasource/utils.ts @@ -15,7 +15,7 @@ import { fetchDataFromAggregateQuery } from './fetch_data_from_aggregate_query'; import type { IndexPatternRef, - TextBasedLanguagesPersistedState, + TextBasedLanguagesPrivateState, TextBasedLanguagesLayerColumn, } from './types'; @@ -36,7 +36,7 @@ export async function loadIndexPatternRefs( } export async function getStateFromAggregateQuery( - state: TextBasedLanguagesPersistedState, + state: TextBasedLanguagesPrivateState, query: AggregateQuery, dataViews: DataViewsPublicPluginStart, data: DataPublicPluginStart, @@ -45,13 +45,14 @@ export async function getStateFromAggregateQuery( const indexPatternRefs: IndexPatternRef[] = await loadIndexPatternRefs(dataViews); const errors: Error[] = []; const layerIds = Object.keys(state.layers); + const context = state.initialContext; const newLayerId = layerIds.length > 0 ? layerIds[0] : generateId(); // fetch the pattern from the query const indexPattern = getIndexPatternFromTextBasedQuery(query); // get the id of the dataview const index = indexPatternRefs.find((r) => r.title === indexPattern)?.id ?? ''; let columnsFromQuery: DatatableColumn[] = []; - let columns: TextBasedLanguagesLayerColumn[] = []; + let allColumns: TextBasedLanguagesLayerColumn[] = []; let timeFieldName; try { const table = await fetchDataFromAggregateQuery(query, dataViews, data, expressions); @@ -59,7 +60,8 @@ export async function getStateFromAggregateQuery( timeFieldName = dataView.timeFieldName; columnsFromQuery = table?.columns ?? []; const existingColumns = state.layers[newLayerId].allColumns; - columns = [ + + allColumns = [ ...existingColumns, ...columnsFromQuery.map((c) => ({ columnId: c.id, fieldName: c.id, meta: c.meta })), ]; @@ -73,7 +75,7 @@ export async function getStateFromAggregateQuery( index, query, columns: state.layers[newLayerId].columns ?? [], - allColumns: columns, + allColumns, timeField: timeFieldName, errors, }, @@ -84,6 +86,7 @@ export async function getStateFromAggregateQuery( ...tempState, fieldList: columnsFromQuery ?? [], indexPatternRefs, + initialContext: context, }; } diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index 0a7f075d50bd..be1f22311050 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -26,12 +26,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'spaceSelector', 'header', ]); + const monacoEditor = getService('monacoEditor'); + + const defaultSettings = { + 'discover:enableSql': true, + }; async function setDiscoverTimeRange() { await PageObjects.timePicker.setDefaultAbsoluteRange(); } describe('discover field visualize button', () => { + before(async () => { + await kibanaServer.uiSettings.replace(defaultSettings); + }); beforeEach(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load( @@ -95,5 +103,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(selectedPattern).to.eql('logst*'); }); }); + + it('should visualize correctly text based language queries', async () => { + await PageObjects.discover.selectTextBaseLang('SQL'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await monacoEditor.setCodeEditorValue( + 'SELECT extension, AVG("bytes") as average FROM "logstash-*" GROUP BY extension' + ); + await testSubjects.click('querySubmitButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + await testSubjects.click('textBased-visualize'); + + await retry.try(async () => { + const dimensions = await testSubjects.findAll('lns-dimensionTrigger-textBased'); + expect(dimensions).to.have.length(2); + expect(await dimensions[1].getVisibleText()).to.be('average'); + }); + }); }); } From a1759bdd183038c5f425be134b422a4166b5e5cc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 30 Sep 2022 10:55:07 +0100 Subject: [PATCH 150/185] [ML] api/ml/jobs/jobs api tests (#142234) * [ML] /api/ml/jobs/jobs api tests * adding space tests --- .../api_integration/apis/ml/jobs/index.ts | 1 + .../test/api_integration/apis/ml/jobs/jobs.ts | 210 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 x-pack/test/api_integration/apis/ml/jobs/jobs.ts diff --git a/x-pack/test/api_integration/apis/ml/jobs/index.ts b/x-pack/test/api_integration/apis/ml/jobs/index.ts index e530b6bfb362..8152f3e760b6 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/index.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/index.ts @@ -23,5 +23,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./stop_datafeeds')); loadTestFile(require.resolve('./stop_datafeeds_spaces')); loadTestFile(require.resolve('./get_groups')); + loadTestFile(require.resolve('./jobs')); }); } diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs.ts new file mode 100644 index 000000000000..c843aab29dc4 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs.ts @@ -0,0 +1,210 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 type { CombinedJobWithStats } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { MULTI_METRIC_JOB_CONFIG, SINGLE_METRIC_JOB_CONFIG, DATAFEED_CONFIG } from './common_jobs'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const idSpace1 = 'space1'; + + const testSetupJobConfigs = [SINGLE_METRIC_JOB_CONFIG, MULTI_METRIC_JOB_CONFIG]; + + const testSetupJobConfigsWithSpace = [ + { ...SINGLE_METRIC_JOB_CONFIG, job_id: `${SINGLE_METRIC_JOB_CONFIG.job_id}-${idSpace1}` }, + ]; + + const testCalendarsConfigs = [ + { + calendar_id: `test_get_cal_1`, + job_ids: ['multi-metric'], + description: `Test calendar 1`, + }, + { + calendar_id: `test_get_cal_2`, + job_ids: [MULTI_METRIC_JOB_CONFIG.job_id, 'multi-metric'], + description: `Test calendar 2`, + }, + { + calendar_id: `test_get_cal_3`, + job_ids: ['brand-new-group'], + description: `Test calendar 3`, + }, + ]; + + async function runGetJobsRequest( + user: USER, + requestBody: object, + expectedResponsecode: number, + space?: string + ): Promise { + const path = space === undefined ? '/api/ml/jobs/jobs' : `/s/${space}/api/ml/jobs/jobs`; + const { body, status } = await supertest + .post(path) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_REQUEST_HEADERS) + .send(requestBody); + ml.api.assertResponseStatusCode(expectedResponsecode, status, body); + + return body; + } + + const expectedJobProperties = [ + { + jobId: MULTI_METRIC_JOB_CONFIG.job_id, + datafeedId: `datafeed-${MULTI_METRIC_JOB_CONFIG.job_id}`, + calendarIds: ['test_get_cal_1', 'test_get_cal_2'], + groups: ['farequote', 'automated', 'multi-metric'], + modelBytes: 0, + datafeedTotalSearchTimeMs: 0, + }, + { + jobId: SINGLE_METRIC_JOB_CONFIG.job_id, + datafeedId: `datafeed-${SINGLE_METRIC_JOB_CONFIG.job_id}`, + calendarIds: undefined, + groups: ['farequote', 'automated', 'single-metric'], + modelBytes: 0, + datafeedTotalSearchTimeMs: 0, + }, + ]; + + const expectedJobPropertiesWithSpace = [ + { + jobId: `${SINGLE_METRIC_JOB_CONFIG.job_id}-${idSpace1}`, + datafeedId: `datafeed-${SINGLE_METRIC_JOB_CONFIG.job_id}-${idSpace1}`, + calendarIds: undefined, + groups: ['farequote', 'automated', 'single-metric'], + modelBytes: 0, + datafeedTotalSearchTimeMs: 0, + }, + ]; + + describe('get combined jobs with stats', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + for (const job of testSetupJobConfigs) { + await ml.api.createAnomalyDetectionJob(job); + await ml.api.createDatafeed({ + ...DATAFEED_CONFIG, + datafeed_id: `datafeed-${job.job_id}`, + job_id: job.job_id, + }); + } + + for (const job of testSetupJobConfigsWithSpace) { + await ml.api.createAnomalyDetectionJob(job, idSpace1); + await ml.api.createDatafeed( + { + ...DATAFEED_CONFIG, + datafeed_id: `datafeed-${job.job_id}`, + job_id: job.job_id, + }, + idSpace1 + ); + } + + for (const cal of testCalendarsConfigs) { + await ml.api.createCalendar(cal.calendar_id, cal); + } + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('returns expected list of combined jobs with stats in default space', async () => { + const jobs = await runGetJobsRequest(USER.ML_VIEWER, {}, 200); + + expect(jobs.length).to.eql( + testSetupJobConfigs.length, + `number of jobs in default space should be ${testSetupJobConfigs.length})` + ); + + jobs.forEach((job, i) => { + expect(job.job_id).to.eql( + expectedJobProperties[i].jobId, + `job id should be equal to ${JSON.stringify(expectedJobProperties[i].jobId)})` + ); + expect(job.datafeed_config.datafeed_id).to.eql( + expectedJobProperties[i].datafeedId, + `datafeed id should be equal to ${JSON.stringify(expectedJobProperties[i].datafeedId)})` + ); + expect(job.calendars).to.eql( + expectedJobProperties[i].calendarIds, + `calendars should be equal to ${JSON.stringify(expectedJobProperties[i].calendarIds)})` + ); + expect(job.groups).to.eql( + expectedJobProperties[i].groups, + `groups should be equal to ${JSON.stringify(expectedJobProperties[i].groups)})` + ); + expect(job.model_size_stats.model_bytes).to.eql( + expectedJobProperties[i].modelBytes, + `model_bytes should be equal to ${JSON.stringify(expectedJobProperties[i].modelBytes)})` + ); + expect(job.datafeed_config.timing_stats.total_search_time_ms).to.eql( + expectedJobProperties[i].datafeedTotalSearchTimeMs, + `datafeed total_search_time_ms should be equal to ${JSON.stringify( + expectedJobProperties[i].datafeedTotalSearchTimeMs + )})` + ); + }); + }); + + it('returns expected list of combined jobs with stats in specified space', async () => { + const jobs = await runGetJobsRequest(USER.ML_VIEWER, {}, 200, idSpace1); + + expect(jobs.length).to.eql( + testSetupJobConfigsWithSpace.length, + `number of jobs in default space should be ${testSetupJobConfigsWithSpace.length})` + ); + + jobs.forEach((job, i) => { + expect(job.job_id).to.eql( + expectedJobPropertiesWithSpace[i].jobId, + `job id should be equal to ${JSON.stringify(expectedJobPropertiesWithSpace[i].jobId)})` + ); + expect(job.datafeed_config.datafeed_id).to.eql( + expectedJobPropertiesWithSpace[i].datafeedId, + `datafeed id should be equal to ${JSON.stringify( + expectedJobPropertiesWithSpace[i].datafeedId + )})` + ); + expect(job.calendars).to.eql( + expectedJobPropertiesWithSpace[i].calendarIds, + `calendars should be equal to ${JSON.stringify( + expectedJobPropertiesWithSpace[i].calendarIds + )})` + ); + expect(job.groups).to.eql( + expectedJobPropertiesWithSpace[i].groups, + `groups should be equal to ${JSON.stringify(expectedJobPropertiesWithSpace[i].groups)})` + ); + expect(job.model_size_stats.model_bytes).to.eql( + expectedJobPropertiesWithSpace[i].modelBytes, + `model_bytes should be equal to ${JSON.stringify( + expectedJobPropertiesWithSpace[i].modelBytes + )})` + ); + expect(job.datafeed_config.timing_stats.total_search_time_ms).to.eql( + expectedJobPropertiesWithSpace[i].datafeedTotalSearchTimeMs, + `datafeed total_search_time_ms should be equal to ${JSON.stringify( + expectedJobPropertiesWithSpace[i].datafeedTotalSearchTimeMs + )})` + ); + }); + }); + }); +}; From 6b11352f719ab5756c260b4a3f10afa22daba86a Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Fri, 30 Sep 2022 11:03:40 +0100 Subject: [PATCH 151/185] [Security Solution][Detections] fixes validation issues in rules actions form (#141811) ## Summary - addresses https://github.com/elastic/kibana/issues/140593 - bulk edit of rules actions - [single rule actions update](https://github.com/elastic/kibana/issues/140593#issuecomment-1257903212 ) ### Before Single rule actions update https://user-images.githubusercontent.com/92328789/192327231-8fdc846c-55f2-4ab1-8786-e96d2376af48.mov Bulk edit rule actions https://user-images.githubusercontent.com/92328789/192327094-ea830769-9633-43dc-be37-7ec68de4bd6f.mp4 ### After Single rule actions update and Bulk edit rule actions https://user-images.githubusercontent.com/92328789/192325274-010d11fc-17eb-47a1-b817-7a24eba8a365.mov --- .../rules/rule_actions_field/index.tsx | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 0babfc8de387..4cc5aed8aab6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -86,8 +86,7 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = updatedActions[index] = deepMerge(updatedActions[index], { id }); field.setValue(updatedActions); }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [field.setValue, actions] + [field, actions] ); const setAlertActionsProperty = useCallback( @@ -98,20 +97,26 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = const setActionParamsProperty = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (key: string, value: any, index: number) => { - field.setValue((prevValue: RuleAction[]) => { - const updatedActions = [...prevValue]; - updatedActions[index] = { - ...updatedActions[index], - params: { - ...updatedActions[index].params, - [key]: value, - }, - }; - return updatedActions; - }); + // validation is not triggered correctly when actions params updated (more details in https://github.com/elastic/kibana/issues/142217) + // wrapping field.setValue in setTimeout fixes the issue above + // and triggers validation after params have been updated + setTimeout( + () => + field.setValue((prevValue: RuleAction[]) => { + const updatedActions = [...prevValue]; + updatedActions[index] = { + ...updatedActions[index], + params: { + ...updatedActions[index].params, + [key]: value, + }, + }; + return updatedActions; + }), + 0 + ); }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [field.setValue] + [field] ); const actionForm = useMemo( From f6149a610f5654ed0d7be57c10bf12e71aab0bf0 Mon Sep 17 00:00:00 2001 From: Yngrid Coello Date: Fri, 30 Sep 2022 12:36:12 +0200 Subject: [PATCH 152/185] Removed isBeta property from SettingsSection interface since it was not used anymore in the template (#142313) --- .../components/fleet_integration/apm_policy_form/index.tsx | 1 - .../fleet_integration/apm_policy_form/settings_form/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx index 8a8a44282fb7..bad43923934a 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx @@ -133,7 +133,6 @@ export function APMPolicyForm({ vars = {}, updateAPMPolicy }: Props) { } ), settings: tailSamplingSettings, - isBeta: false, isPlatinumLicence: true, }, ] diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx index a834c4109879..d32ace193342 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx @@ -92,7 +92,6 @@ export interface SettingsSection { title: string; subtitle?: string; settings: SettingsRow[]; - isBeta?: boolean; isPlatinumLicence?: boolean; } From d92baddf1726a4c8b48ce08a8ac15180b4d9b84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Fri, 30 Sep 2022 12:39:16 +0200 Subject: [PATCH 153/185] Remove duplicate breadcrumb link from the settings page (#142068) --- .../enterprise_search_content/components/settings/settings.tsx | 3 --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 4 files changed, 6 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx index eaab5d56b1b4..94c4c1fdbaad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx @@ -31,9 +31,6 @@ export const Settings: React.FC = () => { return ( Date: Fri, 30 Sep 2022 04:08:51 -0700 Subject: [PATCH 154/185] Respect `default_field: false` when generating index settings (#142277) --- .../template/default_settings.test.ts | 5 +++++ .../epm/elasticsearch/template/default_settings.ts | 14 ++++++++------ .../fleet/server/services/epm/fields/field.ts | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts index df1c87105bcf..bb2be99b868b 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts @@ -48,6 +48,11 @@ describe('buildDefaultSettings', () => { name: 'field5Wildcard', type: 'wildcard', }, + { + name: 'field6NotDefault', + type: 'keyword', + default_field: false, + }, ], }); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts index 5accf7b120f9..b15eb0111782 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts @@ -11,19 +11,20 @@ import type { Field, Fields } from '../../fields/field'; const QUERY_DEFAULT_FIELD_TYPES = ['keyword', 'text', 'match_only_text', 'wildcard']; const QUERY_DEFAULT_FIELD_LIMIT = 1024; -const flattenFieldsToNameAndType = ( +const flattenAndExtractFields = ( fields: Fields, path: string = '' -): Array> => { - let newFields: Array> = []; +): Array> => { + let newFields: Array> = []; fields.forEach((field) => { const fieldName = path ? `${path}.${field.name}` : field.name; newFields.push({ name: fieldName, type: field.type, + default_field: field.default_field, }); if (field.fields && field.fields.length) { - newFields = newFields.concat(flattenFieldsToNameAndType(field.fields, fieldName)); + newFields = newFields.concat(flattenAndExtractFields(field.fields, fieldName)); } }); return newFields; @@ -45,8 +46,9 @@ export function buildDefaultSettings({ const logger = appContextService.getLogger(); // Find all field names to set `index.query.default_field` to, which will be // the first 1024 keyword or text fields - const defaultFields = flattenFieldsToNameAndType(fields).filter( - (field) => field.type && QUERY_DEFAULT_FIELD_TYPES.includes(field.type) + const defaultFields = flattenAndExtractFields(fields).filter( + (field) => + field.type && QUERY_DEFAULT_FIELD_TYPES.includes(field.type) && field.default_field !== false ); if (defaultFields.length > QUERY_DEFAULT_FIELD_LIMIT) { logger.warn( diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index e970a2fea145..b8af56646389 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -37,6 +37,7 @@ export interface Field { include_in_root?: boolean; null_value?: string; dimension?: boolean; + default_field?: boolean; // Meta fields metric_type?: string; From c23579feb62f4ce452b74ecfb9411c056cef687e Mon Sep 17 00:00:00 2001 From: Faisal Kanout Date: Fri, 30 Sep 2022 14:37:15 +0200 Subject: [PATCH 155/185] [Actionable Observability] - Fix Alert tab goes blank in APM because of Alert Details page feature flag (#142188) * Add the o11y context to the alert table config * Fix checks * Make the feature flag optional * optional config for the check * Made option only for alertDetails --- .../public/config/register_alerts_table_configuration.tsx | 6 ++++-- .../components/alerts_flyout/alerts_flyout_footer.tsx | 2 +- .../alerts/components/observability_actions.test.tsx | 1 + .../pages/alerts/components/observability_actions.tsx | 8 ++++---- .../alerts_table_t_grid/alerts_table_t_grid.tsx | 5 +++-- .../containers/alerts_table_t_grid/get_row_actions.tsx | 7 ++++++- x-pack/plugins/observability/public/plugin.ts | 5 ++++- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx index b9600088af3b..bc41b8d80328 100644 --- a/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx +++ b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx @@ -16,9 +16,11 @@ import { addDisplayNames } from '../pages/alerts/containers/alerts_table_t_grid/ import { columns as alertO11yColumns } from '../pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid'; import { getRowActions } from '../pages/alerts/containers/alerts_table_t_grid/get_row_actions'; import type { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry'; +import type { ConfigSchema } from '../plugin'; const getO11yAlertsTableConfiguration = ( - observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry, + config: ConfigSchema ) => ({ id: observabilityFeatureId, casesFeatureId, @@ -33,7 +35,7 @@ const getO11yAlertsTableConfiguration = ( }, }, ], - useActionsColumn: getRowActions(observabilityRuleTypeRegistry), + useActionsColumn: getRowActions(observabilityRuleTypeRegistry, config), useBulkActions: useBulkAddToCaseActions, useInternalFlyout: () => { const { header, body, footer } = useToGetInternalFlyout(observabilityRuleTypeRegistry); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_footer.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_footer.tsx index 3a6bcb8d28f2..eecadba66b61 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_footer.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_flyout/alerts_flyout_footer.tsx @@ -18,7 +18,7 @@ export default function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { i const { http } = services; const prepend = http?.basePath.prepend; const getAlertDetailsButton = () => { - if (!config.unsafe.alertDetails.enabled || !alert) return <>; + if (!config?.unsafe?.alertDetails.enabled || !alert) return <>; return ( ({ describe('ObservabilityActions component', () => { const setup = async (pageId: string) => { const props: ObservabilityActionsProps = { + config, eventId: '6d4c6d74-d51a-495c-897d-88ced3b95e30', ecsData: { _id: '6d4c6d74-d51a-495c-897d-88ced3b95e30', diff --git a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx index eddead1fa90d..2ebb4629ba7b 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/observability_actions.tsx @@ -19,7 +19,6 @@ import React, { useMemo, useState, useCallback } from 'react'; import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { CommentType } from '@kbn/cases-plugin/common'; import type { ActionProps } from '@kbn/timelines-plugin/common'; -import { usePluginContext } from '../../../hooks/use_plugin_context'; import { useKibana } from '../../../utils/kibana_react'; import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions'; import { parseAlert } from './parse_alert'; @@ -33,6 +32,7 @@ import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types'; import type { TopAlert } from '../containers/alerts_page/types'; import { ObservabilityRuleTypeRegistry } from '../../..'; import { ALERT_DETAILS_PAGE_ID } from '../../alert_details/types'; +import { ConfigSchema } from '../../../plugin'; export type ObservabilityActionsProps = Pick< ActionProps, @@ -41,6 +41,7 @@ export type ObservabilityActionsProps = Pick< setFlyoutAlert: React.Dispatch>; observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; id?: string; + config: ConfigSchema; }; export function ObservabilityActions({ @@ -49,12 +50,12 @@ export function ObservabilityActions({ ecsData, id: pageId, observabilityRuleTypeRegistry, + config, setFlyoutAlert, }: ObservabilityActionsProps) { const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {}); const [openActionsPopoverId, setActionsPopover] = useState(null); const { cases, http } = useKibana().services; - const { config } = usePluginContext(); const parseObservabilityAlert = useMemo( () => parseAlert(observabilityRuleTypeRegistry), @@ -108,7 +109,6 @@ export function ObservabilityActions({ selectCaseModal.open({ attachments: caseAttachments }); closeActionsPopover(); }, [caseAttachments, closeActionsPopover, selectCaseModal]); - const actionsMenuItems = useMemo(() => { return [ ...(userCasesPermissions.create && userCasesPermissions.read @@ -143,7 +143,7 @@ export function ObservabilityActions({ : []), ...[ - config.unsafe.alertDetails.enabled && linkToAlert ? ( + config?.unsafe?.alertDetails.enabled && linkToAlert ? ( ().services; - const { observabilityRuleTypeRegistry } = usePluginContext(); + const { observabilityRuleTypeRegistry, config } = usePluginContext(); const [flyoutAlert, setFlyoutAlert] = useState(undefined); const [tGridState, setTGridState] = useState | null>( @@ -210,13 +210,14 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { setEventsDeleted={setEventsDeleted} setFlyoutAlert={setFlyoutAlert} observabilityRuleTypeRegistry={observabilityRuleTypeRegistry} + config={config} /> ); }, }, ]; - }, [setEventsDeleted, observabilityRuleTypeRegistry]); + }, [setEventsDeleted, observabilityRuleTypeRegistry, config]); const onStateChange = useCallback( (state: TGridState) => { diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx index 26af88484057..0cf6c092dc70 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/get_row_actions.tsx @@ -9,6 +9,7 @@ import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strat import { ObservabilityRuleTypeRegistry } from '../../../../rules/create_observability_rule_type_registry'; import { ObservabilityActions } from '../../components/observability_actions'; import type { ObservabilityActionsProps } from '../../components/observability_actions'; +import type { ConfigSchema } from '../../../../plugin'; const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data'] => { return Object.entries(alerts).reduce( @@ -17,7 +18,10 @@ const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data'] ); }; const fakeSetEventsDeleted = () => []; -export const getRowActions = (observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry) => { +export const getRowActions = ( + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry, + config: ConfigSchema +) => { return () => ({ renderCustomActionsRow: ( alert: EcsFieldsResponse, @@ -33,6 +37,7 @@ export const getRowActions = (observabilityRuleTypeRegistry: ObservabilityRuleTy observabilityRuleTypeRegistry={observabilityRuleTypeRegistry} setEventsDeleted={fakeSetEventsDeleted} setFlyoutAlert={setFlyoutAlert} + config={config} /> ); }, diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 6c84eb2588a0..161fe2851ae3 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -294,7 +294,10 @@ export class Plugin const { getO11yAlertsTableConfiguration } = await import( './config/register_alerts_table_configuration' ); - return getO11yAlertsTableConfiguration(this.observabilityRuleTypeRegistry); + return getO11yAlertsTableConfiguration( + this.observabilityRuleTypeRegistry, + this.initContext.config.get() + ); }; const { alertsTableConfigurationRegistry } = pluginsStart.triggersActionsUi; From cab963cc823db6dd82fb85a850c3698c8be4f346 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 30 Sep 2022 13:41:15 +0100 Subject: [PATCH 156/185] [ML] Api tests for ml/saved_objects/remove_item_from_current_space (#142319) --- .../apis/ml/saved_objects/index.ts | 1 + .../remove_from_current_space.ts | 137 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 x-pack/test/api_integration/apis/ml/saved_objects/remove_from_current_space.ts diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/index.ts b/x-pack/test/api_integration/apis/ml/saved_objects/index.ts index f1464095edff..5139a121e07d 100644 --- a/x-pack/test/api_integration/apis/ml/saved_objects/index.ts +++ b/x-pack/test/api_integration/apis/ml/saved_objects/index.ts @@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./sync_jobs')); loadTestFile(require.resolve('./sync_trained_models')); loadTestFile(require.resolve('./update_jobs_spaces')); + loadTestFile(require.resolve('./remove_from_current_space')); }); } diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/remove_from_current_space.ts b/x-pack/test/api_integration/apis/ml/saved_objects/remove_from_current_space.ts new file mode 100644 index 000000000000..3e98c51ea047 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/saved_objects/remove_from_current_space.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { MlSavedObjectType } from '@kbn/ml-plugin/common/types/saved_objects'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const spacesService = getService('spaces'); + const supertest = getService('supertestWithoutAuth'); + + const adJobId = 'fq_single'; + const dfaJobId = 'ihp_od'; + const trainedModelId = 'trained_model'; + const idSpace1 = 'space1'; + const idSpace2 = 'space2'; + const defaultSpaceId = 'default'; + + async function runRequest( + requestBody: { + ids: string[]; + mlSavedObjectType: MlSavedObjectType; + }, + space: string, + expectedStatusCode: number, + user: USER + ) { + const { body, status } = await supertest + .post(`/s/${space}/api/ml/saved_objects/remove_item_from_current_space`) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_REQUEST_HEADERS) + .send(requestBody); + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + + return body; + } + + describe('POST saved_objects/remove_item_from_current_space', () => { + before(async () => { + await ml.testResources.setKibanaTimeZoneToUTC(); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); + + // create spaces + await spacesService.create({ id: idSpace1, name: 'space_one', disabledFeatures: [] }); + await spacesService.create({ id: idSpace2, name: 'space_two', disabledFeatures: [] }); + + // create anomaly detection job + await ml.api.createAnomalyDetectionJob(ml.commonConfig.getADFqSingleMetricJobConfig(adJobId)); + // create data frame analytics job + await ml.api.createDataFrameAnalyticsJob( + ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(dfaJobId) + ); + // Create trained model + const trainedModelConfig = ml.api.createTestTrainedModelConfig(trainedModelId, 'regression'); + await ml.api.createTrainedModel(trainedModelId, trainedModelConfig.body); + + // reassign spaces for all items + await ml.api.updateJobSpaces(adJobId, 'anomaly-detector', [idSpace1, idSpace2], []); + await ml.api.updateJobSpaces(dfaJobId, 'data-frame-analytics', [idSpace1, idSpace2], []); + await ml.api.updateTrainedModelSpaces(trainedModelId, [idSpace1, idSpace2], []); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.testResources.cleanMLSavedObjects(); + await spacesService.delete(idSpace1); + await spacesService.delete(idSpace2); + }); + + it('should remove AD job from current space', async () => { + await ml.api.assertJobSpaces(adJobId, 'anomaly-detector', [ + defaultSpaceId, + idSpace1, + idSpace2, + ]); + const mlSavedObjectType = 'anomaly-detector'; + const body = await runRequest( + { + ids: [adJobId], + mlSavedObjectType, + }, + idSpace1, + 200, + USER.ML_POWERUSER + ); + + expect(body).to.eql({ [adJobId]: { success: true, type: mlSavedObjectType } }); + await ml.api.assertJobSpaces(adJobId, mlSavedObjectType, [defaultSpaceId, idSpace2]); + }); + + it('should remove DFA job from current space', async () => { + await ml.api.assertJobSpaces(dfaJobId, 'data-frame-analytics', [ + defaultSpaceId, + idSpace1, + idSpace2, + ]); + const mlSavedObjectType = 'data-frame-analytics'; + const body = await runRequest( + { + ids: [dfaJobId], + mlSavedObjectType, + }, + idSpace2, + 200, + USER.ML_POWERUSER + ); + + expect(body).to.eql({ [dfaJobId]: { success: true, type: mlSavedObjectType } }); + await ml.api.assertJobSpaces(dfaJobId, mlSavedObjectType, [defaultSpaceId, idSpace1]); + }); + + it('should remove trained model from current space', async () => { + await ml.api.assertTrainedModelSpaces(trainedModelId, [defaultSpaceId, idSpace1, idSpace2]); + const mlSavedObjectType = 'trained-model'; + const body = await runRequest( + { + ids: [trainedModelId], + mlSavedObjectType, + }, + idSpace2, + 200, + USER.ML_POWERUSER + ); + + expect(body).to.eql({ [trainedModelId]: { success: true, type: mlSavedObjectType } }); + await ml.api.assertTrainedModelSpaces(trainedModelId, [defaultSpaceId, idSpace1]); + }); + }); +}; From d600d3e31f50e1f664e7252ad0459abd9ae9004d Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Fri, 30 Sep 2022 07:58:18 -0500 Subject: [PATCH 157/185] [TIP] Reorganize barchart folder within indicators module (#141856) --- .../public/common/utils/barchart.test.ts | 75 ------------------- .../public/common/utils/barchart.ts | 30 -------- .../__snapshots__/wrapper.test.tsx.snap} | 0 .../__snapshots__/barchart.test.tsx.snap} | 0 .../barchart/barchart.stories.tsx} | 8 +- .../barchart/barchart.test.tsx} | 6 +- .../barchart/barchart.tsx} | 6 +- .../index.tsx => barchart/barchart/index.ts} | 2 +- .../field_selector.test.tsx.snap} | 0 .../field_selector.stories.tsx} | 4 +- .../field_selector/field_selector.test.tsx} | 4 +- .../field_selector/field_selector.tsx} | 4 +- .../field_selector/index.ts} | 2 +- .../field_selector}/styles.ts | 0 .../index.tsx => barchart/index.ts} | 2 +- .../barchart/legend_action/index.ts | 8 ++ .../legend_action/legend_action.tsx} | 6 +- .../wrapper.stories.tsx} | 8 +- .../wrapper.test.tsx} | 5 +- .../wrapper.tsx} | 8 +- .../public/modules/indicators/hooks/index.ts | 11 +++ .../hooks/use_aggregated_indicators.test.tsx | 2 +- .../hooks/use_aggregated_indicators.ts | 10 ++- .../modules/indicators/indicators_page.tsx | 2 +- .../fetch_aggregated_indicators.test.ts | 71 +++++++++++++++++- .../services/fetch_aggregated_indicators.ts | 19 ++++- .../modules/indicators/services/index.ts | 9 +++ 27 files changed, 155 insertions(+), 147 deletions(-) delete mode 100644 x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts delete mode 100644 x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap => barchart/__snapshots__/wrapper.test.tsx.snap} (100%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap => barchart/barchart/__snapshots__/barchart.test.tsx.snap} (100%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart/indicators_barchart.stories.tsx => barchart/barchart/barchart.stories.tsx} (87%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart/indicators_barchart.test.tsx => barchart/barchart/barchart.test.tsx} (88%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart/indicators_barchart.tsx => barchart/barchart/barchart.tsx} (89%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart/index.tsx => barchart/barchart/index.ts} (86%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap => barchart/field_selector/__snapshots__/field_selector.test.tsx.snap} (100%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector/indicators_field_selector.stories.tsx => barchart/field_selector/field_selector.stories.tsx} (90%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector/indicators_field_selector.test.tsx => barchart/field_selector/field_selector.test.tsx} (90%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector/indicators_field_selector.tsx => barchart/field_selector/field_selector.tsx} (94%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector/index.tsx => barchart/field_selector/index.ts} (84%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_field_selector => barchart/field_selector}/styles.ts (100%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart_wrapper/index.tsx => barchart/index.ts} (84%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/index.ts rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicator_barchart_legend_action/indicator_barchart_legend_action.tsx => barchart/legend_action/legend_action.tsx} (89%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx => barchart/wrapper.stories.tsx} (96%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx => barchart/wrapper.test.tsx} (96%) rename x-pack/plugins/threat_intelligence/public/modules/indicators/components/{indicators_barchart_wrapper/indicators_barchart_wrapper.tsx => barchart/wrapper.tsx} (90%) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/index.ts create mode 100644 x-pack/plugins/threat_intelligence/public/modules/indicators/services/index.ts diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts deleted file mode 100644 index 004071059a73..000000000000 --- a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.test.ts +++ /dev/null @@ -1,75 +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 { Aggregation } from '../../modules/indicators/services/fetch_aggregated_indicators'; -import { convertAggregationToChartSeries } from './barchart'; - -const aggregation1: Aggregation = { - events: { - buckets: [ - { - doc_count: 0, - key: 1641016800000, - key_as_string: '1 Jan 2022 06:00:00 GMT', - }, - { - doc_count: 10, - key: 1641038400000, - key_as_string: '1 Jan 2022 12:00:00 GMT', - }, - ], - }, - doc_count: 0, - key: '[Filebeat] AbuseCH Malware', -}; -const aggregation2: Aggregation = { - events: { - buckets: [ - { - doc_count: 20, - key: 1641016800000, - key_as_string: '1 Jan 2022 06:00:00 GMT', - }, - { - doc_count: 8, - key: 1641038400000, - key_as_string: '1 Jan 2022 12:00:00 GMT', - }, - ], - }, - doc_count: 0, - key: '[Filebeat] AbuseCH MalwareBazaar', -}; - -describe('barchart', () => { - describe('convertAggregationToChartSeries', () => { - it('should convert Aggregation[] to ChartSeries[]', () => { - expect(convertAggregationToChartSeries([aggregation1, aggregation2])).toEqual([ - { - x: '1 Jan 2022 06:00:00 GMT', - y: 0, - g: '[Filebeat] AbuseCH Malware', - }, - { - x: '1 Jan 2022 12:00:00 GMT', - y: 10, - g: '[Filebeat] AbuseCH Malware', - }, - { - x: '1 Jan 2022 06:00:00 GMT', - y: 20, - g: '[Filebeat] AbuseCH MalwareBazaar', - }, - { - x: '1 Jan 2022 12:00:00 GMT', - y: 8, - g: '[Filebeat] AbuseCH MalwareBazaar', - }, - ]); - }); - }); -}); diff --git a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts b/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts deleted file mode 100644 index c994e7e9f3a3..000000000000 --- a/x-pack/plugins/threat_intelligence/public/common/utils/barchart.ts +++ /dev/null @@ -1,30 +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 { - Aggregation, - AggregationValue, - ChartSeries, -} from '../../modules/indicators/services/fetch_aggregated_indicators'; - -/** - * Converts data received from an Elastic search with date_histogram aggregation enabled to something usable in the "@elastic/chart" BarChart component - * @param aggregations An array of {@link Aggregation} objects to process - * @returns An array of {@link ChartSeries} directly usable in a BarChart component - */ -export const convertAggregationToChartSeries = (aggregations: Aggregation[]): ChartSeries[] => - aggregations.reduce( - (accumulated: ChartSeries[], current: Aggregation) => - accumulated.concat( - current.events.buckets.map((val: AggregationValue) => ({ - x: val.key_as_string, - y: val.doc_count, - g: current.key, - })) - ), - [] - ); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/__snapshots__/wrapper.test.tsx.snap similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/__snapshots__/indicators_barchart_wrapper.test.tsx.snap rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/__snapshots__/wrapper.test.tsx.snap diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/__snapshots__/barchart.test.tsx.snap similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/__snapshots__/indicators_barchart.test.tsx.snap rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/__snapshots__/barchart.test.tsx.snap diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.stories.tsx similarity index 87% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.stories.tsx index bb0a1b1205b5..0a8831cfc6ff 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.stories.tsx @@ -9,10 +9,10 @@ import moment from 'moment'; import React from 'react'; import { Story } from '@storybook/react'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; -import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; -import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; -import { IndicatorsBarChart } from './indicators_barchart'; -import { ChartSeries } from '../../services/fetch_aggregated_indicators'; +import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers'; +import { mockKibanaTimelinesService } from '../../../../../common/mocks/mock_kibana_timelines_service'; +import { IndicatorsBarChart } from '.'; +import { ChartSeries } from '../../../services'; const mockIndicators: ChartSeries[] = [ { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.test.tsx similarity index 88% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.test.tsx index 19542bdf200a..4a95c0ec890f 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.test.tsx @@ -9,9 +9,9 @@ import moment from 'moment-timezone'; import React from 'react'; import { render } from '@testing-library/react'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; -import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { IndicatorsBarChart } from './indicators_barchart'; -import { ChartSeries } from '../../services/fetch_aggregated_indicators'; +import { TestProvidersComponent } from '../../../../../common/mocks/test_providers'; +import { IndicatorsBarChart } from '.'; +import { ChartSeries } from '../../../services'; moment.suppressDeprecationWarnings = true; moment.tz.setDefault('UTC'); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx similarity index 89% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx index d5535f53a862..2f4fecbd930a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/indicators_barchart.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx @@ -9,9 +9,9 @@ import React, { VFC } from 'react'; import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; import { EuiThemeProvider } from '@elastic/eui'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; -import { IndicatorBarchartLegendAction } from '../indicator_barchart_legend_action/indicator_barchart_legend_action'; -import { barChartTimeAxisLabelFormatter } from '../../../../common/utils/dates'; -import type { ChartSeries } from '../../services/fetch_aggregated_indicators'; +import { IndicatorBarchartLegendAction } from '../legend_action'; +import { barChartTimeAxisLabelFormatter } from '../../../../../common/utils/dates'; +import type { ChartSeries } from '../../../services'; const ID = 'tiIndicator'; const DEFAULT_CHART_HEIGHT = '200px'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/index.ts similarity index 86% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/index.ts index 673d2532ad17..6c9a52cae6c9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicators_barchart'; +export * from './barchart'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/__snapshots__/field_selector.test.tsx.snap similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/__snapshots__/indicators_field_selector.test.tsx.snap rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/__snapshots__/field_selector.test.tsx.snap diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.stories.tsx similarity index 90% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.stories.tsx index 4c7b9d67ea92..e58dc9a7dcc8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.stories.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { Story } from '@storybook/react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; -import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; -import { IndicatorsFieldSelector } from './indicators_field_selector'; +import { RawIndicatorFieldId } from '../../../../../../common/types/indicator'; +import { IndicatorsFieldSelector } from '.'; const mockIndexPattern: DataView = { fields: [ diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.test.tsx similarity index 90% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.test.tsx index 0d62bbcef621..bc41fc26c241 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { render } from '@testing-library/react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; -import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { IndicatorsFieldSelector } from './indicators_field_selector'; +import { TestProvidersComponent } from '../../../../../common/mocks/test_providers'; +import { IndicatorsFieldSelector } from '.'; const mockIndexPattern: DataView = { fields: [ diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.tsx similarity index 94% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.tsx index df45a003cf16..2707ba250784 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/field_selector.tsx @@ -10,8 +10,8 @@ import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DataViewField } from '@kbn/data-views-plugin/common'; import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; -import { SecuritySolutionDataViewBase } from '../../../../types'; -import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; +import { SecuritySolutionDataViewBase } from '../../../../../types'; +import { RawIndicatorFieldId } from '../../../../../../common/types/indicator'; import { useStyles } from './styles'; export const DROPDOWN_TEST_ID = 'tiIndicatorFieldSelectorDropdown'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/index.ts similarity index 84% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/index.ts index 34c531bfc0b3..23409f2df282 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicators_field_selector'; +export * from './field_selector'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/styles.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/styles.ts similarity index 100% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/styles.ts rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/field_selector/styles.ts diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/index.ts similarity index 84% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/index.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/index.ts index 252dab479912..77ebb9fb329c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/index.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './indicators_barchart_wrapper'; +export * from './wrapper'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/index.ts new file mode 100644 index 000000000000..ca82d7dc3a46 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './legend_action'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx similarity index 89% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx index fa573e981f6c..b792d4e22258 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicator_barchart_legend_action/indicator_barchart_legend_action.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/legend_action/legend_action.tsx @@ -8,9 +8,9 @@ import React, { useState, VFC } from 'react'; import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FilterInContextMenu } from '../../../query_bar/components/filter_in'; -import { FilterOutContextMenu } from '../../../query_bar/components/filter_out'; -import { AddToTimelineContextMenu } from '../../../timeline/components/add_to_timeline'; +import { FilterInContextMenu } from '../../../../query_bar/components/filter_in'; +import { FilterOutContextMenu } from '../../../../query_bar/components/filter_out'; +import { AddToTimelineContextMenu } from '../../../../timeline/components/add_to_timeline'; export const POPOVER_BUTTON_TEST_ID = 'tiBarchartPopoverButton'; export const TIMELINE_BUTTON_TEST_ID = 'tiBarchartTimelineButton'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.stories.tsx similarity index 96% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.stories.tsx index ed7be8b06b4a..74fd94d6d562 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.stories.tsx @@ -17,12 +17,8 @@ import { IUiSettingsClient } from '@kbn/core/public'; import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { mockKibanaTimelinesService } from '../../../../common/mocks/mock_kibana_timelines_service'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; -import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; -import { - Aggregation, - AGGREGATION_NAME, - ChartSeries, -} from '../../services/fetch_aggregated_indicators'; +import { IndicatorsBarChartWrapper } from '.'; +import { Aggregation, AGGREGATION_NAME, ChartSeries } from '../../services'; export default { component: IndicatorsBarChartWrapper, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.test.tsx similarity index 96% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.test.tsx index ef968be02c22..577afefe8083 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.test.tsx @@ -10,10 +10,7 @@ import { render } from '@testing-library/react'; import { TimeRange } from '@kbn/es-query'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; -import { - CHART_UPDATE_PROGRESS_TEST_ID, - IndicatorsBarChartWrapper, -} from './indicators_barchart_wrapper'; +import { CHART_UPDATE_PROGRESS_TEST_ID, IndicatorsBarChartWrapper } from '.'; import { DEFAULT_TIME_RANGE } from '../../../query_bar/hooks/use_filters/utils'; import moment from 'moment'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.tsx similarity index 90% rename from x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx rename to x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.tsx index aabfecde3024..57ec76d17bd4 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/wrapper.tsx @@ -19,9 +19,9 @@ import { TimeRange } from '@kbn/es-query'; import { TimeRangeBounds } from '@kbn/data-plugin/common'; import { SecuritySolutionDataViewBase } from '../../../../types'; import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; -import { IndicatorsFieldSelector } from '../indicators_field_selector/indicators_field_selector'; -import { IndicatorsBarChart } from '../indicators_barchart/indicators_barchart'; -import { ChartSeries } from '../../services/fetch_aggregated_indicators'; +import { IndicatorsFieldSelector } from './field_selector'; +import { IndicatorsBarChart } from './barchart'; +import { ChartSeries } from '../../services'; const DEFAULT_FIELD = RawIndicatorFieldId.Feed; @@ -33,7 +33,7 @@ export interface IndicatorsBarChartWrapperProps { */ timeRange?: TimeRange; /** - * List of fields coming from the Security Solution sourcerer data view, passed down to the {@link IndicatorFieldSelector} to populate the dropdown. + * List of fields coming from the Security Solution sourcerer data view, passed down to the {@link IndicatorsFieldSelector} to populate the dropdown. */ indexPattern: SecuritySolutionDataViewBase; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/index.ts new file mode 100644 index 000000000000..23461fc80995 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_aggregated_indicators'; +export * from './use_indicators_filters_context'; +export * from './use_indicators_total_count'; +export * from './use_sourcerer_data_view'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx index 21bff01fab76..f067df33d79d 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx @@ -12,7 +12,7 @@ import { mockedTimefilterService, TestProvidersComponent, } from '../../../common/mocks/test_providers'; -import { createFetchAggregatedIndicators } from '../services/fetch_aggregated_indicators'; +import { createFetchAggregatedIndicators } from '../services'; jest.mock('../services/fetch_aggregated_indicators'); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts index 06b99202288b..dd603d387c17 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts @@ -13,7 +13,7 @@ import { useInspector } from '../../../hooks/use_inspector'; import { RawIndicatorFieldId } from '../../../../common/types/indicator'; import { useKibana } from '../../../hooks/use_kibana'; import { DEFAULT_TIME_RANGE } from '../../query_bar/hooks/use_filters/utils'; -import { useSourcererDataView } from './use_sourcerer_data_view'; +import { useSourcererDataView } from '.'; import { ChartSeries, createFetchAggregatedIndicators, @@ -22,11 +22,17 @@ import { export interface UseAggregatedIndicatorsParam { /** - * From and To values passed to the {@link }useAggregatedIndicators} hook + * From and To values passed to the {@link useAggregatedIndicators} hook * to query indicators for the Indicators barchart. */ timeRange?: TimeRange; + /** + * Filters data passed to the {@link useAggregatedIndicators} hook to query indicators. + */ filters: Filter[]; + /** + * Query data passed to the {@link useAggregatedIndicators} hook to query indicators. + */ filterQuery: Query; } diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx index 5ad041300338..e0b5158274c1 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/indicators_page.tsx @@ -7,7 +7,7 @@ import React, { FC, VFC } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { IndicatorsBarChartWrapper } from './components/indicators_barchart_wrapper/indicators_barchart_wrapper'; +import { IndicatorsBarChartWrapper } from './components/barchart'; import { IndicatorsTable } from './components/indicators_table/indicators_table'; import { useIndicators } from './hooks/use_indicators'; import { DefaultPageLayout } from '../../components/layout'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts index c5503f1b32a0..007623943c53 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.test.ts @@ -8,12 +8,54 @@ import { mockedQueryService, mockedSearchService } from '../../../common/mocks/test_providers'; import { BehaviorSubject, throwError } from 'rxjs'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; -import { AGGREGATION_NAME, createFetchAggregatedIndicators } from './fetch_aggregated_indicators'; +import { + Aggregation, + AGGREGATION_NAME, + convertAggregationToChartSeries, + createFetchAggregatedIndicators, +} from '.'; const aggregationResponse = { rawResponse: { aggregations: { [AGGREGATION_NAME]: { buckets: [] } } }, }; +const aggregation1: Aggregation = { + events: { + buckets: [ + { + doc_count: 0, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 10, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH Malware', +}; +const aggregation2: Aggregation = { + events: { + buckets: [ + { + doc_count: 20, + key: 1641016800000, + key_as_string: '1 Jan 2022 06:00:00 GMT', + }, + { + doc_count: 8, + key: 1641038400000, + key_as_string: '1 Jan 2022 12:00:00 GMT', + }, + ], + }, + doc_count: 0, + key: '[Filebeat] AbuseCH MalwareBazaar', +}; + describe('FetchAggregatedIndicatorsService', () => { beforeEach(jest.clearAllMocks); @@ -115,3 +157,30 @@ describe('FetchAggregatedIndicatorsService', () => { }); }); }); + +describe('convertAggregationToChartSeries', () => { + it('should convert Aggregation[] to ChartSeries[]', () => { + expect(convertAggregationToChartSeries([aggregation1, aggregation2])).toEqual([ + { + x: '1 Jan 2022 06:00:00 GMT', + y: 0, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 10, + g: '[Filebeat] AbuseCH Malware', + }, + { + x: '1 Jan 2022 06:00:00 GMT', + y: 20, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + { + x: '1 Jan 2022 12:00:00 GMT', + y: 8, + g: '[Filebeat] AbuseCH MalwareBazaar', + }, + ]); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts index 6cf0fea18b2c..0edc0cca34e5 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/fetch_aggregated_indicators.ts @@ -9,7 +9,6 @@ import { TimeRangeBounds } from '@kbn/data-plugin/common'; import type { ISearchStart, QueryStart } from '@kbn/data-plugin/public'; import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; -import { convertAggregationToChartSeries } from '../../../common/utils/barchart'; import { calculateBarchartColumnTimeInterval } from '../../../common/utils/dates'; import { RawIndicatorFieldId } from '../../../../common/types/indicator'; import { getIndicatorQueryParams } from '../utils/get_indicator_query_params'; @@ -55,6 +54,24 @@ export interface FetchAggregatedIndicatorsParams { field: string; } +/** + * Converts data received from an Elastic search with date_histogram aggregation enabled to something usable in the "@elastic/chart" BarChart component + * @param aggregations An array of {@link Aggregation} objects to process + * @returns An array of {@link ChartSeries} directly usable in a BarChart component + */ +export const convertAggregationToChartSeries = (aggregations: Aggregation[]): ChartSeries[] => + aggregations.reduce( + (accumulated: ChartSeries[], current: Aggregation) => + accumulated.concat( + current.events.buckets.map((val: AggregationValue) => ({ + x: val.key_as_string, + y: val.doc_count, + g: current.key, + })) + ), + [] + ); + export const createFetchAggregatedIndicators = ({ inspectorAdapter, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/services/index.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/index.ts new file mode 100644 index 000000000000..3215ff662159 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/services/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './fetch_aggregated_indicators'; +export * from './fetch_indicators'; From 85463eae67cb49b4fbc1e4f2215397c747238d24 Mon Sep 17 00:00:00 2001 From: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Date: Fri, 30 Sep 2022 15:06:42 +0200 Subject: [PATCH 158/185] [Lens] fix drag and drop in SQL languages (#142315) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/droppable/get_drop_props.ts | 4 ++-- .../dimension_panel/droppable/on_drop_handler.ts | 6 +++--- .../lens/public/indexpattern_datasource/indexpattern.tsx | 4 ++-- x-pack/plugins/lens/public/utils.ts | 8 ++++++++ .../lens/public/visualizations/xy/annotations/helpers.tsx | 4 ++-- .../lens/public/visualizations/xy/visualization.tsx | 4 ++-- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts index cb1501d1f0ed..a1f16006fd80 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts @@ -18,7 +18,7 @@ import { getOperationDisplay, hasOperationSupportForMultipleFields, } from '../../operations'; -import { isDraggedField, isOperationFromTheSameGroup } from '../../../utils'; +import { isDraggedDataViewField, isOperationFromTheSameGroup } from '../../../utils'; import { hasField } from '../../pure_utils'; import { DragContextState } from '../../../drag_drop/providers'; import { OperationMetadata, DraggedField } from '../../../types'; @@ -82,7 +82,7 @@ export function getDropProps( dataView: indexPatterns[state.layers[target.layerId].indexPatternId], }; - if (isDraggedField(source)) { + if (isDraggedDataViewField(source)) { return getDropPropsForField({ ...props, source, target: targetProps }); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts index 77444e8ae59e..232c96610f04 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { isDraggedField } from '../../../utils'; +import { isDraggedDataViewField } from '../../../utils'; import { DatasourceDimensionDropHandlerProps, DragDropOperation, @@ -49,7 +49,7 @@ interface DropHandlerProps { export function onDrop(props: DatasourceDimensionDropHandlerProps) { const { target, source, dropType, state, indexPatterns } = props; - if (isDraggedField(source) && isFieldDropType(dropType)) { + if (isDraggedDataViewField(source) && isFieldDropType(dropType)) { return onFieldDrop( { ...props, @@ -143,7 +143,7 @@ function onFieldDrop(props: DropHandlerProps, shouldAddField?: boo ); if ( - !isDraggedField(source) || + !isDraggedDataViewField(source) || !newOperation || (shouldAddField && !hasOperationSupportForMultipleFields(indexPattern, targetColumn, undefined, source.field)) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index c16ced9ab78f..fb31c3c1a9a7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -69,7 +69,7 @@ import { isColumnInvalid, cloneLayer, } from './utils'; -import { isDraggedField } from '../utils'; +import { isDraggedDataViewField } from '../utils'; import { normalizeOperationDataType } from './pure_utils'; import { LayerPanel } from './layerpanel'; import { @@ -613,7 +613,7 @@ export function getIndexPatternDatasource({ }; }, getDatasourceSuggestionsForField(state, draggedField, filterLayers, indexPatterns) { - return isDraggedField(draggedField) + return isDraggedDataViewField(draggedField) ? getDatasourceSuggestionsForField( state, draggedField.indexPatternId, diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 181ea104ffa7..c40403cfd092 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -248,6 +248,14 @@ export const DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS = 'lensDontCloseDimensionContainerOnClick'; export function isDraggedField(fieldCandidate: unknown): fieldCandidate is DraggedField { + return ( + typeof fieldCandidate === 'object' && + fieldCandidate !== null && + ['id', 'field'].every((prop) => prop in fieldCandidate) + ); +} + +export function isDraggedDataViewField(fieldCandidate: unknown): fieldCandidate is DraggedField { return ( typeof fieldCandidate === 'object' && fieldCandidate !== null && diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index 3c54d67c4913..990d0dd8f94d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -15,7 +15,7 @@ import { } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { IconChartBarAnnotations } from '@kbn/chart-icons'; -import { isDraggedField } from '../../../utils'; +import { isDraggedDataViewField } from '../../../utils'; import { layerTypes } from '../../../../common'; import type { FramePublicAPI, Visualization } from '../../../types'; import { isHorizontalChart } from '../state_helpers'; @@ -185,7 +185,7 @@ export const onAnnotationDrop: Visualization['onDrop'] = ({ const targetAnnotation = targetLayer.annotations.find(({ id }) => id === target.columnId); const targetDataView = frame.dataViews.indexPatterns[targetLayer.indexPatternId]; - if (isDraggedField(source)) { + if (isDraggedDataViewField(source)) { const timeField = targetDataView.timeFieldName; switch (dropType) { case 'field_add': diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 6184e3c607a8..a5ccfc02b0fa 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -21,7 +21,7 @@ import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { generateId } from '../../id_generator'; import { - isDraggedField, + isDraggedDataViewField, isOperationFromCompatibleGroup, isOperationFromTheSameGroup, renewIDs, @@ -381,7 +381,7 @@ export const getXyVisualization = ({ return; } - if (isDraggedField(dropProps.source)) { + if (isDraggedDataViewField(dropProps.source)) { if (dropProps.source.field.type === 'document') { return; } From 6157f0be8639ec55268ba68ebd3b9f654891c590 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Fri, 30 Sep 2022 08:32:23 -0500 Subject: [PATCH 159/185] [Enterprise Search] Add ML Inference Pipeline - Review Step (#142133) * refactor: ml inference config live validation migration validation from when you click create to be a selector that updates as the form values are updated. This is to make it easier to enable/disable the continue button as we introduce steps to the ml inference modal. * ml inference modal: introduce steps Added footer, steps and placeholder components for the test and review steps of the add ml inference pipeline modal. * refactor: abstract ml inference body gen to common Abstracted the code to generate the ml inference pipeline body from the server to common utility functions. We will need these functions in the frontend to display the JSON for review. And we want to use the same code on the frontend and backend. * add ml inference pipeline review Added review component for the ml inference pipeline using a selector in the kea logic to generate the pipeline. In the future this may need to be a part of the state so it can be modified, but for now a selector seemed to fit better when it's read-only. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ml_inference_pipeline/index.test.ts | 52 ++++++ .../common/ml_inference_pipeline/index.ts | 96 +++++++++++ .../common/types/pipelines.ts | 6 + .../add_ml_inference_pipeline_modal.tsx | 158 ++++++++++++++---- .../ml_inference/configure_pipeline.tsx | 7 +- .../ml_inference/ml_inference_logic.ts | 70 ++++++-- .../ml_inference/review_pipeline.tsx | 49 ++++++ .../pipelines/ml_inference/test_pipeline.tsx | 12 ++ .../pipelines/ml_inference/types.ts | 2 + .../pipelines/ml_inference/utils.ts | 35 +++- .../applications/shared/constants/actions.ts | 4 + ...h_ml_inference_pipeline_processors.test.ts | 40 ----- .../fetch_ml_inference_pipeline_processors.ts | 15 +- .../pipelines/create_pipeline_definitions.ts | 53 +----- .../utils/create_ml_inference_pipeline.ts | 2 +- .../utils/ml_inference_pipeline_utils.ts | 8 +- 16 files changed, 443 insertions(+), 166 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts create mode 100644 x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/review_pipeline.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/test_pipeline.tsx diff --git a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts new file mode 100644 index 000000000000..29becfa3e99a --- /dev/null +++ b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { BUILT_IN_MODEL_TAG } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; + +import { getMlModelTypesForModelConfig, BUILT_IN_MODEL_TAG as LOCAL_BUILT_IN_MODEL_TAG } from '.'; + +describe('getMlModelTypesForModelConfig lib function', () => { + const mockModel: MlTrainedModelConfig = { + inference_config: { + ner: {}, + }, + input: { + field_names: [], + }, + model_id: 'test_id', + model_type: 'pytorch', + tags: ['test_tag'], + }; + const builtInMockModel: MlTrainedModelConfig = { + inference_config: { + text_classification: {}, + }, + input: { + field_names: [], + }, + model_id: 'test_id', + model_type: 'lang_ident', + tags: [BUILT_IN_MODEL_TAG], + }; + + it('should return the model type and inference config type', () => { + const expected = ['pytorch', 'ner']; + const response = getMlModelTypesForModelConfig(mockModel); + expect(response.sort()).toEqual(expected.sort()); + }); + + it('should include the built in type', () => { + const expected = ['lang_ident', 'text_classification', BUILT_IN_MODEL_TAG]; + const response = getMlModelTypesForModelConfig(builtInMockModel); + expect(response.sort()).toEqual(expected.sort()); + }); + + it('local BUILT_IN_MODEL_TAG matches ml plugin', () => { + expect(LOCAL_BUILT_IN_MODEL_TAG).toEqual(BUILT_IN_MODEL_TAG); + }); +}); diff --git a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts new file mode 100644 index 000000000000..3bc43fa14d7b --- /dev/null +++ b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { MlInferencePipeline } from '../types/pipelines'; + +// Getting an error importing this from @kbn/ml-plugin/common/constants/data_frame_analytics' +// So defining it locally for now with a test to make sure it matches. +export const BUILT_IN_MODEL_TAG = 'prepackaged'; + +export interface MlInferencePipelineParams { + description?: string; + destinationField: string; + model: MlTrainedModelConfig; + pipelineName: string; + sourceField: string; +} + +/** + * Generates the pipeline body for a machine learning inference pipeline + * @param pipelineConfiguration machine learning inference pipeline configuration parameters + * @returns pipeline body + */ +export const generateMlInferencePipelineBody = ({ + description, + destinationField, + model, + pipelineName, + sourceField, +}: MlInferencePipelineParams): MlInferencePipeline => { + // if model returned no input field, insert a placeholder + const modelInputField = + model.input?.field_names?.length > 0 ? model.input.field_names[0] : 'MODEL_INPUT_FIELD'; + return { + description: description ?? '', + processors: [ + { + remove: { + field: `ml.inference.${destinationField}`, + ignore_missing: true, + }, + }, + { + inference: { + field_map: { + [sourceField]: modelInputField, + }, + model_id: model.model_id, + target_field: `ml.inference.${destinationField}`, + }, + }, + { + append: { + field: '_source._ingest.processors', + value: [ + { + model_version: model.version, + pipeline: pipelineName, + processed_timestamp: '{{{ _ingest.timestamp }}}', + types: getMlModelTypesForModelConfig(model), + }, + ], + }, + }, + ], + version: 1, + }; +}; + +/** + * Parses model types list from the given configuration of a trained machine learning model + * @param trainedModel configuration for a trained machine learning model + * @returns list of model types + */ +export const getMlModelTypesForModelConfig = (trainedModel: MlTrainedModelConfig): string[] => { + if (!trainedModel) return []; + + const isBuiltIn = trainedModel.tags?.includes(BUILT_IN_MODEL_TAG); + + return [ + trainedModel.model_type, + ...Object.keys(trainedModel.inference_config || {}), + ...(isBuiltIn ? [BUILT_IN_MODEL_TAG] : []), + ].filter((type): type is string => type !== undefined); +}; + +export const formatPipelineName = (rawName: string) => + rawName + .trim() + .replace(/\s+/g, '_') // Convert whitespaces to underscores + .toLowerCase(); diff --git a/x-pack/plugins/enterprise_search/common/types/pipelines.ts b/x-pack/plugins/enterprise_search/common/types/pipelines.ts index 269f7149cc7b..8652eb57f9b4 100644 --- a/x-pack/plugins/enterprise_search/common/types/pipelines.ts +++ b/x-pack/plugins/enterprise_search/common/types/pipelines.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; + export interface InferencePipeline { modelState: TrainedModelState; modelStateReason?: string; @@ -19,3 +21,7 @@ export enum TrainedModelState { Started = 'started', Failed = 'failed', } + +export interface MlInferencePipeline extends IngestPipeline { + version?: number; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_pipeline_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_pipeline_modal.tsx index 8b2cfb1d4b5d..916a295df005 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_pipeline_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_pipeline_modal.tsx @@ -15,6 +15,8 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, + EuiStepsHorizontal, + EuiStepsHorizontalProps, EuiModal, EuiModalBody, EuiModalFooter, @@ -26,11 +28,18 @@ import { import { i18n } from '@kbn/i18n'; +import { + BACK_BUTTON_LABEL, + CANCEL_BUTTON_LABEL, + CONTINUE_BUTTON_LABEL, +} from '../../../../../shared/constants'; import { IndexNameLogic } from '../../index_name_logic'; import { ConfigurePipeline } from './configure_pipeline'; -import { MLInferenceLogic, AddInferencePipelineModal } from './ml_inference_logic'; +import { AddInferencePipelineSteps, MLInferenceLogic } from './ml_inference_logic'; import { NoModelsPanel } from './no_models'; +import { ReviewPipeline } from './review_pipeline'; +import { TestPipeline } from './test_pipeline'; interface AddMLInferencePipelineModalProps { onClose: () => void; @@ -64,15 +73,13 @@ export const AddMLInferencePipelineModal: React.FC { - const { pipelineName, modelID, sourceField } = configuration; - return pipelineName.trim().length === 0 || modelID.length === 0 || sourceField.length === 0; -}; - const AddProcessorContent: React.FC = ({ onClose }) => { - const { addInferencePipelineModal, createErrors, supportedMLModels, isLoading } = - useValues(MLInferenceLogic); - const { createPipeline } = useActions(MLInferenceLogic); + const { + createErrors, + supportedMLModels, + isLoading, + addInferencePipelineModal: { step }, + } = useValues(MLInferenceLogic); if (isLoading) { return ( @@ -103,37 +110,126 @@ const AddProcessorContent: React.FC = ({ onClo )} - + + {step === AddInferencePipelineSteps.Configuration && } + {step === AddInferencePipelineSteps.Test && } + {step === AddInferencePipelineSteps.Review && } - - - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.footer.cancel', - { - defaultMessage: 'Cancel', - } - )} + + + ); +}; + +const ModalSteps: React.FC = () => { + const { + addInferencePipelineModal: { step }, + isPipelineDataValid, + } = useValues(MLInferenceLogic); + const { setAddInferencePipelineStep } = useActions(MLInferenceLogic); + const navSteps: EuiStepsHorizontalProps['steps'] = [ + { + onClick: () => setAddInferencePipelineStep(AddInferencePipelineSteps.Configuration), + status: isPipelineDataValid ? 'complete' : 'disabled', + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title', + { + defaultMessage: 'Configure', + } + ), + }, + { + onClick: () => setAddInferencePipelineStep(AddInferencePipelineSteps.Test), + status: isPipelineDataValid ? 'incomplete' : 'disabled', + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.test.title', + { + defaultMessage: 'Test', + } + ), + }, + { + onClick: () => setAddInferencePipelineStep(AddInferencePipelineSteps.Review), + status: isPipelineDataValid ? 'incomplete' : 'disabled', + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.review.title', + { + defaultMessage: 'Review', + } + ), + }, + ]; + switch (step) { + case AddInferencePipelineSteps.Configuration: + navSteps[0].status = isPipelineDataValid ? 'complete' : 'current'; + break; + case AddInferencePipelineSteps.Test: + navSteps[1].status = 'current'; + break; + case AddInferencePipelineSteps.Review: + navSteps[2].status = 'current'; + break; + } + return ; +}; + +const ModalFooter: React.FC = ({ onClose }) => { + const { addInferencePipelineModal: modal, isPipelineDataValid } = useValues(MLInferenceLogic); + const { createPipeline, setAddInferencePipelineStep } = useActions(MLInferenceLogic); + + let nextStep: AddInferencePipelineSteps | undefined; + let previousStep: AddInferencePipelineSteps | undefined; + switch (modal.step) { + case AddInferencePipelineSteps.Test: + nextStep = AddInferencePipelineSteps.Review; + previousStep = AddInferencePipelineSteps.Configuration; + break; + case AddInferencePipelineSteps.Review: + previousStep = AddInferencePipelineSteps.Test; + break; + case AddInferencePipelineSteps.Configuration: + nextStep = AddInferencePipelineSteps.Test; + break; + } + return ( + + + + {previousStep !== undefined ? ( + setAddInferencePipelineStep(previousStep as AddInferencePipelineSteps)} + > + {BACK_BUTTON_LABEL} - - + ) : null} + + + + {CANCEL_BUTTON_LABEL} + + + {nextStep !== undefined ? ( setAddInferencePipelineStep(nextStep as AddInferencePipelineSteps)} + disabled={!isPipelineDataValid} > + {CONTINUE_BUTTON_LABEL} + + ) : ( + {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.footer.create', + 'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create', { defaultMessage: 'Create', } )} - - - - + )} + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx index 199d62f914f0..b1d8fd4d074a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx @@ -36,6 +36,7 @@ export const ConfigurePipeline: React.FC = () => { const { destinationField, modelID, pipelineName, sourceField } = configuration; const models = supportedMLModels ?? []; + const nameError = formErrors.pipelineName !== undefined && pipelineName.length > 0; return ( <> @@ -73,7 +74,7 @@ export const ConfigurePipeline: React.FC = () => { } )} helpText={ - formErrors.pipelineName === undefined && + !nameError && i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', { @@ -82,8 +83,8 @@ export const ConfigurePipeline: React.FC = () => { } ) } - error={formErrors.pipelineName} - isInvalid={formErrors.pipelineName !== undefined} + error={nameError && formErrors.pipelineName} + isInvalid={nameError} > void; createApiError: (error: HttpError) => HttpError; createApiSuccess: typeof CreateMlInferencePipelineApiLogic.actions.apiSuccess; createPipeline: () => void; @@ -49,10 +59,10 @@ interface MLInferenceProcessorsActions { makeMappingRequest: typeof MappingsApiLogic.actions.makeRequest; mappingsApiError(error: HttpError): HttpError; mlModelsApiError(error: HttpError): HttpError; - setCreateErrors(errors: string[]): { errors: string[] }; - setFormErrors: (inputErrors: AddInferencePipelineFormErrors) => { - inputErrors: AddInferencePipelineFormErrors; + setAddInferencePipelineStep: (step: AddInferencePipelineSteps) => { + step: AddInferencePipelineSteps; }; + setCreateErrors(errors: string[]): { errors: string[] }; setIndexName: (indexName: string) => { indexName: string }; setInferencePipelineConfiguration: (configuration: InferencePipelineConfiguration) => { configuration: InferencePipelineConfiguration; @@ -62,6 +72,7 @@ interface MLInferenceProcessorsActions { export interface AddInferencePipelineModal { configuration: InferencePipelineConfiguration; indexName: string; + step: AddInferencePipelineSteps; } interface MLInferenceProcessorsValues { @@ -69,8 +80,10 @@ interface MLInferenceProcessorsValues { createErrors: string[]; formErrors: AddInferencePipelineFormErrors; isLoading: boolean; + isPipelineDataValid: boolean; mappingData: typeof MappingsApiLogic.values.data; mappingStatus: Status; + mlInferencePipeline?: MlInferencePipeline; mlModelsData: typeof MLModelsApiLogic.values.data; mlModelsStatus: typeof MLModelsApiLogic.values.apiStatus; sourceFields: string[] | undefined; @@ -83,6 +96,7 @@ export const MLInferenceLogic = kea< actions: { clearFormErrors: true, createPipeline: true, + setAddInferencePipelineStep: (step: AddInferencePipelineSteps) => ({ step }), setCreateErrors: (errors: string[]) => ({ errors }), setFormErrors: (inputErrors: AddInferencePipelineFormErrors) => ({ inputErrors }), setIndexName: (indexName: string) => ({ indexName }), @@ -124,12 +138,6 @@ export const MLInferenceLogic = kea< const { addInferencePipelineModal: { configuration, indexName }, } = values; - const validationErrors = validateInferencePipelineConfiguration(configuration); - if (validationErrors !== undefined) { - actions.setFormErrors(validationErrors); - return; - } - actions.clearFormErrors(); actions.makeCreatePipelineRequest({ indexName, @@ -155,8 +163,10 @@ export const MLInferenceLogic = kea< ...EMPTY_PIPELINE_CONFIGURATION, }, indexName: '', + step: AddInferencePipelineSteps.Configuration, }, { + setAddInferencePipelineStep: (modal, { step }) => ({ ...modal, step }), setIndexName: (modal, { indexName }) => ({ ...modal, indexName }), setInferencePipelineConfiguration: (modal, { configuration }) => ({ ...modal, @@ -171,21 +181,47 @@ export const MLInferenceLogic = kea< setCreateErrors: (_, { errors }) => errors, }, ], - formErrors: [ - {}, - { - clearFormErrors: () => ({}), - setFormErrors: (_, { inputErrors }) => inputErrors, - }, - ], }, selectors: ({ selectors }) => ({ + formErrors: [ + () => [selectors.addInferencePipelineModal], + (modal: AddInferencePipelineModal) => + validateInferencePipelineConfiguration(modal.configuration), + ], isLoading: [ () => [selectors.mlModelsStatus, selectors.mappingStatus], (mlModelsStatus, mappingStatus) => !API_REQUEST_COMPLETE_STATUSES.includes(mlModelsStatus) || !API_REQUEST_COMPLETE_STATUSES.includes(mappingStatus), ], + isPipelineDataValid: [ + () => [selectors.formErrors], + (errors: AddInferencePipelineFormErrors) => Object.keys(errors).length === 0, + ], + mlInferencePipeline: [ + () => [ + selectors.isPipelineDataValid, + selectors.addInferencePipelineModal, + selectors.mlModelsData, + ], + ( + isPipelineDataValid: boolean, + { configuration }: AddInferencePipelineModal, + models: MLInferenceProcessorsValues['mlModelsData'] + ) => { + if (!isPipelineDataValid) return undefined; + const model = models?.find((mlModel) => mlModel.model_id === configuration.modelID); + if (!model) return undefined; + + return generateMlInferencePipelineBody({ + destinationField: + configuration.destinationField || formatPipelineName(configuration.pipelineName), + model, + pipelineName: configuration.pipelineName, + sourceField: configuration.sourceField, + }); + }, + ], sourceFields: [ () => [selectors.mappingStatus, selectors.mappingData], (status: Status, mapping: IndicesGetMappingIndexMappingRecord) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/review_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/review_pipeline.tsx new file mode 100644 index 000000000000..580a5acaac2c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/review_pipeline.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { EuiCodeBlock, EuiSpacer, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { MLInferenceLogic } from './ml_inference_logic'; + +export const ReviewPipeline: React.FC = () => { + const { mlInferencePipeline } = useValues(MLInferenceLogic); + return ( + <> + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.review.title', + { + defaultMessage: 'Pipeline configuration', + } + )} +

    +
    + + {JSON.stringify(mlInferencePipeline ?? {}, null, 2)} + + + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.review.description', + { + defaultMessage: + "This pipeline will be created and injected as a processor into your default pipeline for this index. You'll be able to use this new pipeline independently as well.", + } + )} +

    +
    + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/test_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/test_pipeline.tsx new file mode 100644 index 000000000000..523973ad2d3d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/test_pipeline.tsx @@ -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 React from 'react'; + +export const TestPipeline: React.FC = () => { + return
    Test Pipeline
    ; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts index 9ada53d224c8..29ad5e9193fd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts @@ -14,5 +14,7 @@ export interface InferencePipelineConfiguration { export interface AddInferencePipelineFormErrors { destinationField?: string; + modelID?: string; pipelineName?: string; + sourceField?: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts index b788a522d395..8e168586a481 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts @@ -41,17 +41,34 @@ export const isValidPipelineName = (input: string): boolean => { return input.length > 0 && VALID_PIPELINE_NAME_REGEX.test(input); }; +const INVALID_PIPELINE_NAME_ERROR = i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName', + { + defaultMessage: 'Name must only contain letters, numbers, underscores, and hyphens.', + } +); +const FIELD_REQUIRED_ERROR = i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.emptyValueError', + { + defaultMessage: 'Field is required.', + } +); + export const validateInferencePipelineConfiguration = ( config: InferencePipelineConfiguration -): AddInferencePipelineFormErrors | undefined => { +): AddInferencePipelineFormErrors => { const errors: AddInferencePipelineFormErrors = {}; - if (!isValidPipelineName(config.pipelineName)) { - errors.pipelineName = i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName', - { - defaultMessage: 'Name must only contain letters, numbers, underscores, and hyphens.', - } - ); - return errors; + if (config.pipelineName.trim().length === 0) { + errors.pipelineName = FIELD_REQUIRED_ERROR; + } else if (!isValidPipelineName(config.pipelineName)) { + errors.pipelineName = INVALID_PIPELINE_NAME_ERROR; } + if (config.modelID.trim().length === 0) { + errors.modelID = FIELD_REQUIRED_ERROR; + } + if (config.sourceField.trim().length === 0) { + errors.sourceField = FIELD_REQUIRED_ERROR; + } + + return errors; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/constants/actions.ts b/x-pack/plugins/enterprise_search/public/applications/shared/constants/actions.ts index d43217fba144..f163158462f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/constants/actions.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/constants/actions.ts @@ -45,6 +45,10 @@ export const CONTINUE_BUTTON_LABEL = i18n.translate( { defaultMessage: 'Continue' } ); +export const BACK_BUTTON_LABEL = i18n.translate('xpack.enterpriseSearch.actions.backButtonLabel', { + defaultMessage: 'Back', +}); + export const CLOSE_BUTTON_LABEL = i18n.translate( 'xpack.enterpriseSearch.actions.closeButtonLabel', { defaultMessage: 'Close' } diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts index 79d4600bb31e..bb3324cf641d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.test.ts @@ -5,15 +5,12 @@ * 2.0. */ -import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ElasticsearchClient } from '@kbn/core/server'; -import { BUILT_IN_MODEL_TAG } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; import { InferencePipeline, TrainedModelState } from '../../../common/types/pipelines'; import { fetchAndAddTrainedModelData, - getMlModelTypesForModelConfig, getMlModelConfigsForModelIds, fetchMlInferencePipelineProcessorNames, fetchMlInferencePipelineProcessors, @@ -320,43 +317,6 @@ describe('fetchPipelineProcessorInferenceData lib function', () => { }); }); -describe('getMlModelTypesForModelConfig lib function', () => { - const mockModel: MlTrainedModelConfig = { - inference_config: { - ner: {}, - }, - input: { - field_names: [], - }, - model_id: 'test_id', - model_type: 'pytorch', - tags: ['test_tag'], - }; - const builtInMockModel: MlTrainedModelConfig = { - inference_config: { - text_classification: {}, - }, - input: { - field_names: [], - }, - model_id: 'test_id', - model_type: 'lang_ident', - tags: [BUILT_IN_MODEL_TAG], - }; - - it('should return the model type and inference config type', () => { - const expected = ['pytorch', 'ner']; - const response = getMlModelTypesForModelConfig(mockModel); - expect(response.sort()).toEqual(expected.sort()); - }); - - it('should include the built in type', () => { - const expected = ['lang_ident', 'text_classification', BUILT_IN_MODEL_TAG]; - const response = getMlModelTypesForModelConfig(builtInMockModel); - expect(response.sort()).toEqual(expected.sort()); - }); -}); - describe('getMlModelConfigsForModelIds lib function', () => { const mockClient = { ml: { diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts index 95839a9b6ac2..72867ad71706 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_ml_inference_pipeline_processors.ts @@ -5,10 +5,9 @@ * 2.0. */ -import { MlTrainedModelConfig } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ElasticsearchClient } from '@kbn/core/server'; -import { BUILT_IN_MODEL_TAG } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; +import { getMlModelTypesForModelConfig } from '../../../common/ml_inference_pipeline'; import { InferencePipeline, TrainedModelState } from '../../../common/types/pipelines'; import { getInferencePipelineNameFromIndexName } from '../../utils/ml_inference_pipeline_utils'; @@ -70,18 +69,6 @@ export const fetchPipelineProcessorInferenceData = async ( ); }; -export const getMlModelTypesForModelConfig = (trainedModel: MlTrainedModelConfig): string[] => { - if (!trainedModel) return []; - - const isBuiltIn = trainedModel.tags?.includes(BUILT_IN_MODEL_TAG); - - return [ - trainedModel.model_type, - ...Object.keys(trainedModel.inference_config || {}), - ...(isBuiltIn ? [BUILT_IN_MODEL_TAG] : []), - ].filter((type): type is string => type !== undefined); -}; - export const getMlModelConfigsForModelIds = async ( client: ElasticsearchClient, trainedModelNames: string[] diff --git a/x-pack/plugins/enterprise_search/server/lib/pipelines/create_pipeline_definitions.ts b/x-pack/plugins/enterprise_search/server/lib/pipelines/create_pipeline_definitions.ts index bcb33f9f82b3..f32590fb516c 100644 --- a/x-pack/plugins/enterprise_search/server/lib/pipelines/create_pipeline_definitions.ts +++ b/x-pack/plugins/enterprise_search/server/lib/pipelines/create_pipeline_definitions.ts @@ -5,20 +5,16 @@ * 2.0. */ -import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core/server'; +import { generateMlInferencePipelineBody } from '../../../common/ml_inference_pipeline'; +import { MlInferencePipeline } from '../../../common/types/pipelines'; import { getInferencePipelineNameFromIndexName } from '../../utils/ml_inference_pipeline_utils'; -import { getMlModelTypesForModelConfig } from '../indices/fetch_ml_inference_pipeline_processors'; export interface CreatedPipelines { created: string[]; } -export interface MlInferencePipeline extends IngestPipeline { - version?: number; -} - /** * Used to create index-specific Ingest Pipelines to be used in conjunction with Enterprise Search * ingestion mechanisms. Three pipelines are created: @@ -237,43 +233,10 @@ export const formatMlPipelineBody = async ( // this will raise a 404 if model doesn't exist const models = await esClient.ml.getTrainedModels({ model_id: modelId }); const model = models.trained_model_configs[0]; - // if model returned no input field, insert a placeholder - const modelInputField = - model.input?.field_names?.length > 0 ? model.input.field_names[0] : 'MODEL_INPUT_FIELD'; - const modelTypes = getMlModelTypesForModelConfig(model); - const modelVersion = model.version; - return { - description: '', - processors: [ - { - remove: { - field: `ml.inference.${destinationField}`, - ignore_missing: true, - }, - }, - { - inference: { - field_map: { - [sourceField]: modelInputField, - }, - model_id: modelId, - target_field: `ml.inference.${destinationField}`, - }, - }, - { - append: { - field: '_source._ingest.processors', - value: [ - { - model_version: modelVersion, - pipeline: pipelineName, - processed_timestamp: '{{{ _ingest.timestamp }}}', - types: modelTypes, - }, - ], - }, - }, - ], - version: 1, - }; + return generateMlInferencePipelineBody({ + destinationField, + model, + pipelineName, + sourceField, + }); }; diff --git a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts index dfcd8d488497..da4bce12d71e 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_ml_inference_pipeline.ts @@ -8,6 +8,7 @@ import { IngestGetPipelineResponse, IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core/server'; +import { formatPipelineName } from '../../common/ml_inference_pipeline'; import { ErrorCode } from '../../common/types/error_codes'; import { formatMlPipelineBody } from '../lib/pipelines/create_pipeline_definitions'; @@ -15,7 +16,6 @@ import { formatMlPipelineBody } from '../lib/pipelines/create_pipeline_definitio import { getInferencePipelineNameFromIndexName, getPrefixedInferencePipelineProcessorName, - formatPipelineName, } from './ml_inference_pipeline_utils'; /** diff --git a/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts b/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts index 6547034f2540..e92db3d263a6 100644 --- a/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts +++ b/x-pack/plugins/enterprise_search/server/utils/ml_inference_pipeline_utils.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { formatPipelineName } from '../../common/ml_inference_pipeline'; + export const getInferencePipelineNameFromIndexName = (indexName: string) => `${indexName}@ml-inference`; @@ -12,9 +14,3 @@ export const getPrefixedInferencePipelineProcessorName = (pipelineName: string) pipelineName.startsWith('ml-inference-') ? formatPipelineName(pipelineName) : `ml-inference-${formatPipelineName(pipelineName)}`; - -export const formatPipelineName = (rawName: string) => - rawName - .trim() - .replace(/\s+/g, '_') // Convert whitespaces to underscores - .toLowerCase(); From 067484b9b3d1c21f9d596a85b0537727a7747229 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 30 Sep 2022 16:48:09 +0300 Subject: [PATCH 160/185] [Lens] don't allow to drag and drop a single element (#141793) * [wip][Lens] don't allow to drag and drop a single element * do some cleanup * push some changes * update field_inputs * some cleanup * fix styles * push some code * push some changes * fix some tests * push some changes * add padding * fix styles * first attempt at cleaning up markup and styles * change the name of color to bgColor, pass isDragging state * Update default_bucket_container.tsx more overlooked copy * fix JEST * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx Co-authored-by: Michael Marcialis * Update x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx Co-authored-by: Michael Marcialis * fix JEST Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Michael Marcialis Co-authored-by: Marta Bondyra Co-authored-by: Michael Marcialis --- .../definitions/filters/filters.test.tsx | 4 +- .../definitions/filters/filters.tsx | 10 +- .../definitions/ranges/advanced_editor.tsx | 22 +- .../definitions/ranges/ranges.test.tsx | 4 +- .../definitions/terms/field_inputs.tsx | 239 ++++++------------ .../drag_drop_bucket/buckets.test.tsx | 5 +- .../drag_drop_bucket/buckets.tsx | 206 ++++++--------- .../default_bucket_container.tsx | 93 +++++++ .../fields_bucket_container.tsx | 86 +++++++ .../drag_drop_bucket/index.tsx | 11 + .../drag_drop_bucket/new_bucket_button.tsx | 37 +++ .../drag_drop_bucket/types.ts | 23 ++ .../lens/public/shared_components/index.ts | 3 +- .../annotations_config_panel/index.scss | 7 +- .../tooltip_annotation_panel.tsx | 172 +++++-------- .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 18 files changed, 494 insertions(+), 437 deletions(-) create mode 100644 x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx create mode 100644 x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx create mode 100644 x-pack/plugins/lens/public/shared_components/drag_drop_bucket/index.tsx create mode 100644 x-pack/plugins/lens/public/shared_components/drag_drop_bucket/new_bucket_button.tsx create mode 100644 x-pack/plugins/lens/public/shared_components/drag_drop_bucket/types.ts diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index 299896e5ffe6..2b227674c26e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -399,8 +399,8 @@ describe('filters', () => { ); instance - .find('[data-test-subj="lns-customBucketContainer-remove"]') - .at(2) + .find('[data-test-subj="lns-customBucketContainer-remove-1"]') + .at(0) .simulate('click'); expect(updateLayerSpy).toHaveBeenCalledWith({ ...layer, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index b972129109e1..434dbb092bdb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -235,13 +235,14 @@ export const FilterList = ({ droppableId="FILTERS_DROPPABLE_AREA" items={localFilters} > - {localFilters?.map((filter: FilterValue, idx: number) => { + {localFilters?.map((filter, idx, arrayRef) => { const isInvalid = !isQueryValid(filter.input, indexPattern); + const id = filter.id; return ( { + const addNewRange = useCallback(() => { const newRangeId = generateId(); setLocalRanges([ @@ -228,13 +228,13 @@ export const AdvancedRangeEditor = ({ { id: newRangeId, from: localRanges[localRanges.length - 1].to, - to: Infinity, + to: Number.POSITIVE_INFINITY, label: '', }, ]); setActiveRangeId(newRangeId); - }; + }, [localRanges]); const changeActiveRange = (rangeId: string) => { let newActiveRangeId = rangeId; @@ -264,11 +264,10 @@ export const AdvancedRangeEditor = ({ <> {}} droppableId="RANGES_DROPPABLE_AREA" items={localRanges} > - {localRanges.map((range: LocalRangeType, idx: number) => ( + {localRanges.map((range, idx, arrayRef) => ( { - const newRanges = localRanges.filter((_, i) => i !== idx); + const newRanges = arrayRef.filter((_, i) => i !== idx); setLocalRanges(newRanges); }} removeTitle={i18n.translate('xpack.lens.indexPattern.ranges.deleteRange', { defaultMessage: 'Delete range', })} - isNotRemovable={localRanges.length === 1} + isNotRemovable={arrayRef.length === 1} + isNotDraggable={arrayRef.length < 2} > changeActiveRange('')} setRange={(newRange: LocalRangeType) => { - const newRanges = [...localRanges]; + const newRanges = [...arrayRef]; if (newRange.id === newRanges[idx].id) { newRanges[idx] = newRange; } else { @@ -320,9 +320,7 @@ export const AdvancedRangeEditor = ({ ))} { - addNewRange(); - }} + onClick={addNewRange} label={i18n.translate('xpack.lens.indexPattern.ranges.addRange', { defaultMessage: 'Add range', })} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 907a9f685215..874559ae66c1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -797,8 +797,8 @@ describe('ranges', () => { // This series of act closures are made to make it work properly the update flush act(() => { instance - .find('[data-test-subj="lns-customBucketContainer-remove"]') - .last() + .find('[data-test-subj="lns-customBucketContainer-remove-1"]') + .at(0) .simulate('click'); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx index f140fdb9807a..d22fc5392f2c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx @@ -5,24 +5,16 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; -import { - EuiButtonIcon, - EuiDraggable, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - htmlIdGenerator, - EuiPanel, - useEuiTheme, -} from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { htmlIdGenerator } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ExistingFieldsMap, IndexPattern } from '../../../../types'; import { DragDropBuckets, + FieldsBucketContainer, NewBucketButton, - TooltipWrapper, useDebouncedValue, + DraggableBucketContainer, } from '../../../../shared_components'; import { FieldSelect } from '../../../dimension_panel/field_select'; import type { TermsIndexPatternColumn } from './types'; @@ -61,9 +53,6 @@ export function FieldInputs({ operationSupportMatrix, invalidFields, }: FieldInputsProps) { - const { euiTheme } = useEuiTheme(); - const [isDragging, setIsDragging] = useState(false); - const onChangeWrapped = useCallback( (values: WrappedValue[]) => onChange(values.filter(removeNewEmptyField).map(({ value }) => value)), @@ -97,154 +86,90 @@ export function FieldInputs({ ); const disableActions = - (localValues.length === 2 && localValues.some(({ isNew }) => isNew)) || - localValues.length === 1; + localValues.length === 1 || localValues.filter(({ isNew }) => !isNew).length < 2; const localValuesFilled = localValues.filter(({ isNew }) => !isNew); return ( <> -
    { + handleInputChange(updatedValues); }} + droppableId="TOP_TERMS_DROPPABLE_AREA" + items={localValues} + bgColor="subdued" > - { - handleInputChange(updatedValues); - setIsDragging(false); - }} - className="lnsIndexPatternDimensionEditor__droppable" - onDragStart={() => { - setIsDragging(true); - }} - droppableId="TOP_TERMS_DROPPABLE_AREA" - items={localValues} - > - {localValues.map(({ id, value, isNew }, index) => { - // need to filter the available fields for multiple terms - // * a scripted field should be removed - // * a field of unsupported type should be removed - // * a field that has been used - // * a scripted field was used in a singular term, should be marked as invalid for multi-terms - const filteredOperationByField = Object.keys(operationSupportMatrix.operationByField) - .filter((key) => { - if (key === value) { - return true; - } - const field = indexPattern.getFieldByName(key); - if (index === 0) { - return !rawValuesLookup.has(key) && field && supportedTypes.has(field.type); - } else { - return ( - !rawValuesLookup.has(key) && - field && - !field.scripted && - supportedTypes.has(field.type) - ); - } - }) - .reduce((memo, key) => { - memo[key] = operationSupportMatrix.operationByField[key]; - return memo; - }, {}); + {localValues.map(({ id, value, isNew }, index, arrayRef) => { + // need to filter the available fields for multiple terms + // * a scripted field should be removed + // * a field of unsupported type should be removed + // * a field that has been used + // * a scripted field was used in a singular term, should be marked as invalid for multi-terms + const filteredOperationByField = Object.keys(operationSupportMatrix.operationByField) + .filter((key) => { + if (key === value) { + return true; + } + const field = indexPattern.getFieldByName(key); + if (index === 0) { + return !rawValuesLookup.has(key) && field && supportedTypes.has(field.type); + } else { + return ( + !rawValuesLookup.has(key) && + field && + !field.scripted && + supportedTypes.has(field.type) + ); + } + }) + .reduce((memo, key) => { + memo[key] = operationSupportMatrix.operationByField[key]; + return memo; + }, {}); - const shouldShowError = Boolean( - value && - ((indexPattern.getFieldByName(value)?.scripted && localValuesFilled.length > 1) || - invalidFields?.includes(value)) - ); - return ( - - {(provided) => ( - - - - - - - { - onFieldSelectChange(choice, index); - }} - isInvalid={shouldShowError} - data-test-subj={ - localValues.length !== 1 - ? `indexPattern-dimension-field-${index}` - : undefined - } - /> - - - - { - handleInputChange(localValues.filter((_, i) => i !== index)); - }} - data-test-subj={`indexPattern-terms-removeField-${index}`} - isDisabled={disableActions && !isNew} - /> - - - - - )} - - ); - })} - -
    + const shouldShowError = Boolean( + value && + ((indexPattern.getFieldByName(value)?.scripted && localValuesFilled.length > 1) || + invalidFields?.includes(value)) + ); + const itemId = (value ?? 'newField') + id; + + return ( + { + handleInputChange(arrayRef.filter((_, i) => i !== index)); + }} + removeTitle={i18n.translate('xpack.lens.indexPattern.terms.deleteButtonLabel', { + defaultMessage: 'Delete', + })} + isNotRemovable={disableActions && !isNew} + isNotDraggable={arrayRef.length < 2} + data-test-subj={`indexPattern-terms`} + Container={FieldsBucketContainer} + isInsidePanel={true} + > + { + onFieldSelectChange(choice, index); + }} + isInvalid={shouldShowError} + data-test-subj={ + localValues.length !== 1 ? `indexPattern-dimension-field-${index}` : undefined + } + /> + + ); + })} + { handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx index aba0cbfc40a6..2336495c9a31 100644 --- a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.test.tsx @@ -63,14 +63,13 @@ describe('buckets shared components', () => { it('should render invalid component', () => { const instance = mount(); const iconProps = instance.find(EuiIcon).first().props(); - expect(iconProps.color).toEqual('danger'); + expect(iconProps.color).toEqual('#BD271E'); expect(iconProps.type).toEqual('alert'); - expect(iconProps.title).toEqual('invalid'); }); it('should call onRemoveClick when remove icon is clicked', () => { const instance = mount(); const removeIcon = instance - .find('[data-test-subj="lns-customBucketContainer-remove"]') + .find('[data-test-subj="lns-customBucketContainer-remove-0"]') .first(); removeIcon.simulate('click'); expect(defaultProps.onRemoveClick).toHaveBeenCalled(); diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx index 720d8c85ca48..7d0893cdd54e 100644 --- a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/buckets.tsx @@ -5,162 +5,110 @@ * 2.0. */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { useCallback, useState } from 'react'; +import type { Assign } from '@kbn/utility-types'; import { - EuiFlexGroup, - EuiFlexItem, EuiPanel, - EuiButtonIcon, - EuiIcon, - EuiDragDropContext, - euiDragDropReorder, EuiDraggable, EuiDroppable, - EuiButtonEmpty, + EuiPanelProps, + EuiDragDropContext, + DragDropContextProps, + euiDragDropReorder, + useEuiTheme, } from '@elastic/eui'; - -export const NewBucketButton = ({ - label, - onClick, - ['data-test-subj']: dataTestSubj, - isDisabled, - className, -}: { - label: string; - onClick: () => void; - 'data-test-subj'?: string; - isDisabled?: boolean; - className?: string; -}) => ( - - {label} - -); - -interface BucketContainerProps { - isInvalid?: boolean; - invalidMessage: string; - onRemoveClick: () => void; - removeTitle: string; - isNotRemovable?: boolean; - children: React.ReactNode; - dataTestSubj?: string; -} - -const BucketContainer = ({ - isInvalid, - invalidMessage, - onRemoveClick, - removeTitle, - children, - dataTestSubj, - isNotRemovable, -}: BucketContainerProps) => { - return ( - - - {/* Empty for spacing */} - - - - {children} - - - - - - ); -}; +import { DefaultBucketContainer } from './default_bucket_container'; +import type { BucketContainerProps } from './types'; export const DraggableBucketContainer = ({ id, - idx, children, + isInsidePanel, + Container = DefaultBucketContainer, ...bucketContainerProps -}: { - id: string; - idx: number; - children: React.ReactNode; -} & BucketContainerProps) => { +}: Assign< + Omit, + { + id: string; + children: React.ReactNode; + isInsidePanel?: boolean; + Container?: React.FunctionComponent; + } +>) => { + const { euiTheme } = useEuiTheme(); + return ( - {(provided) => {children}} + {(provided, state) => ( + + {children} + + )} ); }; -interface DraggableLocation { - droppableId: string; - index: number; -} - -export const DragDropBuckets = ({ +export function DragDropBuckets({ items, onDragStart, onDragEnd, droppableId, children, - className, + bgColor, }: { - items: any; // eslint-disable-line @typescript-eslint/no-explicit-any - onDragStart: () => void; - onDragEnd: (items: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + items: T[]; droppableId: string; children: React.ReactElement[]; - className?: string; -}) => { - const handleDragEnd = ({ - source, - destination, - }: { - source?: DraggableLocation; - destination?: DraggableLocation; - }) => { - if (source && destination) { - const newItems = euiDragDropReorder(items, source.index, destination.index); - onDragEnd(newItems); - } - }; + onDragStart?: () => void; + onDragEnd?: (items: T[]) => void; + bgColor?: EuiPanelProps['color']; +}) { + const [isDragging, setIsDragging] = useState(false); + + const handleDragEnd: DragDropContextProps['onDragEnd'] = useCallback( + ({ source, destination }) => { + setIsDragging(false); + if (source && destination) { + onDragEnd?.(euiDragDropReorder(items, source.index, destination.index)); + } + }, + [items, onDragEnd] + ); + + const handleDragStart: DragDropContextProps['onDragStart'] = useCallback(() => { + setIsDragging(true); + onDragStart?.(); + }, [onDragStart]); + return ( - - - {children} - + + + + {children} + + ); -}; +} diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx new file mode 100644 index 000000000000..bfae243dc1ac --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/default_bucket_container.tsx @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + useEuiTheme, +} from '@elastic/eui'; +import type { BucketContainerProps } from './types'; +import { TooltipWrapper } from '../tooltip_wrapper'; + +export const DefaultBucketContainer = ({ + idx, + isInvalid, + invalidMessage, + onRemoveClick, + removeTitle, + children, + draggableProvided, + isNotRemovable, + isNotDraggable, + 'data-test-subj': dataTestSubj = 'lns-customBucketContainer', +}: BucketContainerProps) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + + + + + {children} + + + + + + + + ); +}; diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx new file mode 100644 index 000000000000..eedcba3fee5e --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/fields_bucket_container.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + useEuiTheme, +} from '@elastic/eui'; +import { TooltipWrapper } from '..'; +import type { BucketContainerProps } from './types'; + +export const FieldsBucketContainer = ({ + idx, + onRemoveClick, + removeTitle, + children, + draggableProvided, + isNotRemovable, + isNotDraggable, + isDragging, + 'data-test-subj': dataTestSubj = 'lns-fieldsBucketContainer', +}: BucketContainerProps) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + + + + + + {children} + + + + + + + + + ); +}; diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/index.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/index.tsx new file mode 100644 index 000000000000..127471dc2cca --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/index.tsx @@ -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. + */ + +export { NewBucketButton } from './new_bucket_button'; +export { FieldsBucketContainer } from './fields_bucket_container'; + +export * from './buckets'; diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/new_bucket_button.tsx b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/new_bucket_button.tsx new file mode 100644 index 000000000000..38b74ca7c83f --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/new_bucket_button.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; + +interface NewBucketButtonProps { + label: string; + onClick: () => void; + isDisabled?: boolean; + className?: string; + 'data-test-subj'?: string; +} + +export const NewBucketButton = ({ + label, + onClick, + isDisabled, + className, + 'data-test-subj': dataTestSubj = 'lns-newBucket-add', +}: NewBucketButtonProps) => ( + + {label} + +); diff --git a/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/types.ts b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/types.ts new file mode 100644 index 000000000000..fe08048e875c --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/drag_drop_bucket/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { DraggableProvided } from 'react-beautiful-dnd'; + +export interface BucketContainerProps { + children: React.ReactNode; + removeTitle: string; + idx: number; + onRemoveClick: () => void; + isDragging?: boolean; + draggableProvided?: DraggableProvided; + isInvalid?: boolean; + invalidMessage?: string; + isNotRemovable?: boolean; + isNotDraggable?: boolean; + 'data-test-subj'?: string; +} diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts index 3f30eb64ff2c..a2fcc9c54882 100644 --- a/x-pack/plugins/lens/public/shared_components/index.ts +++ b/x-pack/plugins/lens/public/shared_components/index.ts @@ -17,7 +17,8 @@ export { NewBucketButton, DraggableBucketContainer, DragDropBuckets, -} from './drag_drop_bucket/buckets'; + FieldsBucketContainer, +} from './drag_drop_bucket'; export { RangeInputField } from './range_input_field'; export { BucketAxisBoundsControl, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss index 2b59be0b044f..93bf0e2c7266 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss @@ -14,11 +14,6 @@ margin-top: $euiSizeXS; } -.lnsConfigPanelAnnotations__droppable { - padding: $euiSizeXS; - border-radius: $euiBorderRadiusSmall; -} - .lnsConfigPanelAnnotations__fieldPicker { cursor: pointer; -} \ No newline at end of file +} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx index c44f76427e0a..20a99e8458fc 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx @@ -5,19 +5,9 @@ * 2.0. */ -import { - htmlIdGenerator, - EuiButtonIcon, - EuiDraggable, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - useEuiTheme, - EuiText, -} from '@elastic/eui'; +import { htmlIdGenerator, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import type { ExistingFieldsMap, IndexPattern } from '../../../../types'; import { @@ -28,8 +18,12 @@ import { useDebouncedValue, NewBucketButton, DragDropBuckets, + DraggableBucketContainer, + FieldsBucketContainer, } from '../../../../shared_components'; +export const MAX_TOOLTIP_FIELDS_SIZE = 2; + const generateId = htmlIdGenerator(); const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); @@ -60,8 +54,6 @@ export function TooltipSection({ existingFields, invalidFields, }: FieldInputsProps) { - const { euiTheme } = useEuiTheme(); - const [isDragging, setIsDragging] = useState(false); const onChangeWrapped = useCallback( (values: WrappedValue[]) => { setConfig({ @@ -108,6 +100,7 @@ export function TooltipSection({ label={i18n.translate('xpack.lens.xyChart.annotation.tooltip.addField', { defaultMessage: 'Add field', })} + isDisabled={localValues.length > MAX_TOOLTIP_FIELDS_SIZE} /> ); @@ -132,7 +125,7 @@ export function TooltipSection({ ); } const currentExistingField = existingFields[indexPattern.title]; - const disableActions = localValues.length === 2 && localValues.some(({ isNew }) => isNew); + const options = indexPattern.fields .filter( ({ displayName, type }) => @@ -154,107 +147,62 @@ export function TooltipSection({ ) .sort((a, b) => a.label.localeCompare(b.label)); - const isDragDisabled = localValues.length < 2; - return ( <> -
    { + handleInputChange(updatedValues); }} + droppableId="ANNOTATION_TOOLTIP_DROPPABLE_AREA" + items={localValues} + bgColor="subdued" > - { - handleInputChange(updatedValues); - setIsDragging(false); - }} - onDragStart={() => { - setIsDragging(true); - }} - droppableId="ANNOTATION_TOOLTIP_DROPPABLE_AREA" - items={localValues} - className="lnsConfigPanelAnnotations__droppable" - > - {localValues.map(({ id, value, isNew }, index) => { - const fieldIsValid = value ? Boolean(indexPattern.getFieldByName(value)) : true; - return ( - - {(provided) => ( - - - {/* Empty for spacing */} - - - - - - - - { - handleInputChange(localValues.filter((_, i) => i !== index)); - }} - data-test-subj={`lnsXY-annotation-tooltip-removeField-${index}`} - isDisabled={disableActions && !isNew} - /> - - - - )} - - ); - })} - -
    + {localValues.map(({ id, value, isNew }, index, arrayRef) => { + const fieldIsValid = value ? Boolean(indexPattern.getFieldByName(value)) : true; + + return ( + { + handleInputChange(arrayRef.filter((_, i) => i !== index)); + }} + removeTitle={i18n.translate( + 'xpack.lens.xyChart.annotation.tooltip.deleteButtonLabel', + { + defaultMessage: 'Delete', + } + )} + isNotDraggable={arrayRef.length < 2} + Container={FieldsBucketContainer} + isInsidePanel={true} + data-test-subj={`lnsXY-annotation-tooltip-${index}`} + > + { + onFieldSelectChange(choice, index); + }} + fieldIsInvalid={!fieldIsValid} + className="lnsConfigPanelAnnotations__fieldPicker" + data-test-subj={`lnsXY-annotation-tooltip-field-picker--${index}`} + autoFocus={isNew && value == null} + /> + + ); + })} + {newBucketButton} ); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 51944419d001..4ab7756b5fb4 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17838,10 +17838,7 @@ "xpack.lens.indexPattern.terms.addaFilter": "Ajouter un champ", "xpack.lens.indexPattern.terms.addRegex": "Utiliser une expression régulière", "xpack.lens.indexPattern.terms.advancedSettings": "Avancé", - "xpack.lens.indexPattern.terms.deleteButtonAriaLabel": "Supprimer", - "xpack.lens.indexPattern.terms.deleteButtonDisabled": "Cette fonction nécessite au minimum un champ défini.", "xpack.lens.indexPattern.terms.deleteButtonLabel": "Supprimer", - "xpack.lens.indexPattern.terms.dragToReorder": "Faire glisser pour réorganiser", "xpack.lens.indexPattern.terms.exclude": "Exclure les valeurs", "xpack.lens.indexPattern.terms.include": "Inclure les valeurs", "xpack.lens.indexPattern.terms.includeExcludePatternPlaceholder": "Entrer une expression régulière pour filtrer les valeurs", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index be3158df0ac4..c01a643d5e95 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17821,10 +17821,7 @@ "xpack.lens.indexPattern.terms.addaFilter": "フィールドの追加", "xpack.lens.indexPattern.terms.addRegex": "正規表現を使用", "xpack.lens.indexPattern.terms.advancedSettings": "高度な設定", - "xpack.lens.indexPattern.terms.deleteButtonAriaLabel": "削除", - "xpack.lens.indexPattern.terms.deleteButtonDisabled": "この関数には定義された1つのフィールドの最小値が必須です", "xpack.lens.indexPattern.terms.deleteButtonLabel": "削除", - "xpack.lens.indexPattern.terms.dragToReorder": "ドラッグして並べ替え", "xpack.lens.indexPattern.terms.exclude": "値を除外", "xpack.lens.indexPattern.terms.include": "値を含める", "xpack.lens.indexPattern.terms.includeExcludePatternPlaceholder": "値をフィルターするには正規表現を入力します", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f3d3ffc9fb7f..75eab045500e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17846,10 +17846,7 @@ "xpack.lens.indexPattern.terms.addaFilter": "添加字段", "xpack.lens.indexPattern.terms.addRegex": "使用正则表达式", "xpack.lens.indexPattern.terms.advancedSettings": "高级", - "xpack.lens.indexPattern.terms.deleteButtonAriaLabel": "删除", - "xpack.lens.indexPattern.terms.deleteButtonDisabled": "此函数需要至少定义一个字段", "xpack.lens.indexPattern.terms.deleteButtonLabel": "删除", - "xpack.lens.indexPattern.terms.dragToReorder": "拖动以重新排序", "xpack.lens.indexPattern.terms.exclude": "排除值", "xpack.lens.indexPattern.terms.include": "包括值", "xpack.lens.indexPattern.terms.includeExcludePatternPlaceholder": "输入正则表达式以筛选值", From 1854694db8546bc0db402547d73ba23be515bb12 Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Fri, 30 Sep 2022 09:47:21 -0500 Subject: [PATCH 161/185] [Security Solution] add new permission properties for endpoint rbac (#142243) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../endpoint/service/authz/authz.test.ts | 69 +++++++- .../common/endpoint/service/authz/authz.ts | 164 +++++++++++++++++- .../common/endpoint/types/authz.ts | 30 ++++ 3 files changed, 245 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts index d8f65cf07d28..e5b2f78c2547 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.test.ts @@ -110,24 +110,60 @@ describe('Endpoint Authz service', () => { describe('and endpoint rbac is enabled', () => { it.each<[EndpointAuthzKeyList[number], string]>([ + ['canWriteEndpointList', 'writeEndpointList'], + ['canReadEndpointList', 'readEndpointList'], + ['canWritePolicyManagement', 'writePolicyManagement'], + ['canReadPolicyManagement', 'readPolicyManagement'], + ['canWriteActionsLogManagement', 'writeActionsLogManagement'], + ['canReadActionsLogManagement', 'readActionsLogManagement'], ['canIsolateHost', 'writeHostIsolation'], ['canUnIsolateHost', 'writeHostIsolation'], ['canKillProcess', 'writeProcessOperations'], ['canSuspendProcess', 'writeProcessOperations'], ['canGetRunningProcesses', 'writeProcessOperations'], + ['canWriteFileOperations', 'writeFileOperations'], + ['canWriteTrustedApplications', 'writeTrustedApplications'], + ['canReadTrustedApplications', 'readTrustedApplications'], + ['canWriteHostIsolationExceptions', 'writeHostIsolationExceptions'], + ['canReadHostIsolationExceptions', 'readHostIsolationExceptions'], + ['canWriteBlocklist', 'writeBlocklist'], + ['canReadBlocklist', 'readBlocklist'], + ['canWriteEventFilters', 'writeEventFilters'], + ['canReadEventFilters', 'readEventFilters'], ])('%s should be true if `packagePrivilege.%s` is `true`', (auth) => { const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); expect(authz[auth]).toBe(true); }); - it.each<[EndpointAuthzKeyList[number], string]>([ - ['canIsolateHost', 'writeHostIsolation'], - ['canUnIsolateHost', 'writeHostIsolation'], - ['canKillProcess', 'writeProcessOperations'], - ['canSuspendProcess', 'writeProcessOperations'], - ['canGetRunningProcesses', 'writeProcessOperations'], - ])('%s should be false if `packagePrivilege.%s` is `false`', (auth, privilege) => { - fleetAuthz.packagePrivileges!.endpoint.actions[privilege].executePackageAction = false; + it.each<[EndpointAuthzKeyList[number], string[]]>([ + ['canWriteEndpointList', ['writeEndpointList']], + ['canReadEndpointList', ['writeEndpointList', 'readEndpointList']], + ['canWritePolicyManagement', ['writePolicyManagement']], + ['canReadPolicyManagement', ['writePolicyManagement', 'readPolicyManagement']], + ['canWriteActionsLogManagement', ['writeActionsLogManagement']], + ['canReadActionsLogManagement', ['writeActionsLogManagement', 'readActionsLogManagement']], + ['canIsolateHost', ['writeHostIsolation']], + ['canUnIsolateHost', ['writeHostIsolation']], + ['canKillProcess', ['writeProcessOperations']], + ['canSuspendProcess', ['writeProcessOperations']], + ['canGetRunningProcesses', ['writeProcessOperations']], + ['canWriteFileOperations', ['writeFileOperations']], + ['canWriteTrustedApplications', ['writeTrustedApplications']], + ['canReadTrustedApplications', ['writeTrustedApplications', 'readTrustedApplications']], + ['canWriteHostIsolationExceptions', ['writeHostIsolationExceptions']], + [ + 'canReadHostIsolationExceptions', + ['writeHostIsolationExceptions', 'readHostIsolationExceptions'], + ], + ['canWriteBlocklist', ['writeBlocklist']], + ['canReadBlocklist', ['writeBlocklist', 'readBlocklist']], + ['canWriteEventFilters', ['writeEventFilters']], + ['canReadEventFilters', ['writeEventFilters', 'readEventFilters']], + ])('%s should be false if `packagePrivilege.%s` is `false`', (auth, privileges) => { + // read permission checks for write || read so we need to set both to false + privileges.forEach((privilege) => { + fleetAuthz.packagePrivileges!.endpoint.actions[privilege].executePackageAction = false; + }); const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true); expect(authz[auth]).toBe(false); }); @@ -139,13 +175,28 @@ describe('Endpoint Authz service', () => { expect(getEndpointAuthzInitialState()).toEqual({ canAccessFleet: false, canAccessEndpointManagement: false, + canCreateArtifactsByPolicy: false, + canWriteEndpointList: false, + canReadEndpointList: false, + canWritePolicyManagement: false, + canReadPolicyManagement: false, + canWriteActionsLogManagement: false, + canReadActionsLogManagement: false, canIsolateHost: false, canUnIsolateHost: true, - canCreateArtifactsByPolicy: false, canKillProcess: false, canSuspendProcess: false, canGetRunningProcesses: false, canAccessResponseConsole: false, + canWriteFileOperations: false, + canWriteTrustedApplications: false, + canReadTrustedApplications: false, + canWriteHostIsolationExceptions: false, + canReadHostIsolationExceptions: false, + canWriteBlocklist: false, + canReadBlocklist: false, + canWriteEventFilters: false, + canReadEventFilters: false, }); }); }); diff --git a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts index 9e280f383cae..dde2a7f92b1e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/authz/authz.ts @@ -5,11 +5,23 @@ * 2.0. */ -import type { FleetAuthz } from '@kbn/fleet-plugin/common'; +import type { ENDPOINT_PRIVILEGES } from '@kbn/fleet-plugin/common'; +import { type FleetAuthz } from '@kbn/fleet-plugin/common'; import type { LicenseService } from '../../../license'; import type { EndpointAuthz } from '../../types/authz'; import type { MaybeImmutable } from '../../types'; +function hasPermission( + fleetAuthz: FleetAuthz, + isEndpointRbacEnabled: boolean, + hasEndpointManagementAccess: boolean, + privilege: typeof ENDPOINT_PRIVILEGES[number] +) { + return isEndpointRbacEnabled + ? fleetAuthz.packagePrivileges?.endpoint?.actions[privilege].executePackageAction ?? false + : hasEndpointManagementAccess; +} + /** * Used by both the server and the UI to generate the Authorization for access to Endpoint related * functionality @@ -27,19 +39,128 @@ export const calculateEndpointAuthz = ( const isPlatinumPlusLicense = licenseService.isPlatinumPlus(); const isEnterpriseLicense = licenseService.isEnterprise(); const hasEndpointManagementAccess = userRoles.includes('superuser'); - const canIsolateHost = isEndpointRbacEnabled - ? fleetAuthz.packagePrivileges?.endpoint?.actions?.writeHostIsolation?.executePackageAction || - false - : hasEndpointManagementAccess; - const canWriteProcessOperations = isEndpointRbacEnabled - ? fleetAuthz.packagePrivileges?.endpoint?.actions?.writeProcessOperations - ?.executePackageAction || false - : hasEndpointManagementAccess; + const canWriteEndpointList = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeEndpointList' + ); + const canReadEndpointList = + canWriteEndpointList || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readEndpointList' + ); + const canWritePolicyManagement = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writePolicyManagement' + ); + const canReadPolicyManagement = + canWritePolicyManagement || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readPolicyManagement' + ); + const canWriteActionsLogManagement = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeActionsLogManagement' + ); + const canReadActionsLogManagement = + canWriteActionsLogManagement || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readActionsLogManagement' + ); + const canIsolateHost = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeHostIsolation' + ); + const canWriteProcessOperations = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeProcessOperations' + ); + const canWriteTrustedApplications = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeTrustedApplications' + ); + const canReadTrustedApplications = + canWriteTrustedApplications || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readTrustedApplications' + ); + const canWriteHostIsolationExceptions = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeHostIsolationExceptions' + ); + const canReadHostIsolationExceptions = + canWriteHostIsolationExceptions || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readHostIsolationExceptions' + ); + const canWriteBlocklist = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeBlocklist' + ); + const canReadBlocklist = + canWriteBlocklist || + hasPermission(fleetAuthz, isEndpointRbacEnabled, hasEndpointManagementAccess, 'readBlocklist'); + const canWriteEventFilters = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeEventFilters' + ); + const canReadEventFilters = + canWriteEventFilters || + hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'readEventFilters' + ); + const canWriteFileOperations = hasPermission( + fleetAuthz, + isEndpointRbacEnabled, + hasEndpointManagementAccess, + 'writeFileOperations' + ); return { canAccessFleet: fleetAuthz?.fleet.all ?? userRoles.includes('superuser'), canAccessEndpointManagement: hasEndpointManagementAccess, canCreateArtifactsByPolicy: hasEndpointManagementAccess && isPlatinumPlusLicense, + canWriteEndpointList, + canReadEndpointList, + canWritePolicyManagement, + canReadPolicyManagement, + canWriteActionsLogManagement, + canReadActionsLogManagement, // Response Actions canIsolateHost: canIsolateHost && isPlatinumPlusLicense, canUnIsolateHost: canIsolateHost, @@ -47,6 +168,16 @@ export const calculateEndpointAuthz = ( canSuspendProcess: canWriteProcessOperations && isEnterpriseLicense, canGetRunningProcesses: canWriteProcessOperations && isEnterpriseLicense, canAccessResponseConsole: hasEndpointManagementAccess && isEnterpriseLicense, + canWriteFileOperations: canWriteFileOperations && isEnterpriseLicense, + // artifacts + canWriteTrustedApplications, + canReadTrustedApplications, + canWriteHostIsolationExceptions: canWriteHostIsolationExceptions && isPlatinumPlusLicense, + canReadHostIsolationExceptions, + canWriteBlocklist, + canReadBlocklist, + canWriteEventFilters, + canReadEventFilters, }; }; @@ -55,11 +186,26 @@ export const getEndpointAuthzInitialState = (): EndpointAuthz => { canAccessFleet: false, canAccessEndpointManagement: false, canCreateArtifactsByPolicy: false, + canWriteEndpointList: false, + canReadEndpointList: false, + canWritePolicyManagement: false, + canReadPolicyManagement: false, + canWriteActionsLogManagement: false, + canReadActionsLogManagement: false, canIsolateHost: false, canUnIsolateHost: true, canKillProcess: false, canSuspendProcess: false, canGetRunningProcesses: false, canAccessResponseConsole: false, + canWriteFileOperations: false, + canWriteTrustedApplications: false, + canReadTrustedApplications: false, + canWriteHostIsolationExceptions: false, + canReadHostIsolationExceptions: false, + canWriteBlocklist: false, + canReadBlocklist: false, + canWriteEventFilters: false, + canReadEventFilters: false, }; }; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/authz.ts b/x-pack/plugins/security_solution/common/endpoint/types/authz.ts index e237e4ba1fe9..b8ca15d69d1a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/authz.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/authz.ts @@ -16,6 +16,18 @@ export interface EndpointAuthz { canAccessEndpointManagement: boolean; /** if user has permissions to create Artifacts by Policy */ canCreateArtifactsByPolicy: boolean; + /** if user has write permissions to endpoint list */ + canWriteEndpointList: boolean; + /** if user has read permissions to endpoint list */ + canReadEndpointList: boolean; + /** if user has write permissions for policy management */ + canWritePolicyManagement: boolean; + /** if user has read permissions for policy management */ + canReadPolicyManagement: boolean; + /** if user has write permissions for actions log management */ + canWriteActionsLogManagement: boolean; + /** if user has read permissions for actions log management */ + canReadActionsLogManagement: boolean; /** If user has permissions to isolate hosts */ canIsolateHost: boolean; /** If user has permissions to un-isolate (release) hosts */ @@ -28,6 +40,24 @@ export interface EndpointAuthz { canGetRunningProcesses: boolean; /** If user has permissions to use the Response Actions Console */ canAccessResponseConsole: boolean; + /** If user has write permissions to use file operations */ + canWriteFileOperations: boolean; + /** if user has write permissions for trusted applications */ + canWriteTrustedApplications: boolean; + /** if user has read permissions for trusted applications */ + canReadTrustedApplications: boolean; + /** if user has write permissions for host isolation exceptions */ + canWriteHostIsolationExceptions: boolean; + /** if user has read permissions for host isolation exceptions */ + canReadHostIsolationExceptions: boolean; + /** if user has write permissions for blocklist entries */ + canWriteBlocklist: boolean; + /** if user has read permissions for blocklist entries */ + canReadBlocklist: boolean; + /** if user has write permissions for event filters */ + canWriteEventFilters: boolean; + /** if user has read permissions for event filters */ + canReadEventFilters: boolean; } export type EndpointAuthzKeyList = Array; From 62734e6236c8c945db0fc7d6c03d299d0833f61d Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Fri, 30 Sep 2022 16:03:55 +0100 Subject: [PATCH 162/185] Remove output_id from apm package policy (#142331) --- .../runtime_attachment/runtime_attachment.stories.tsx | 2 -- .../apm_policy_form/edit_apm_policy_form.stories.tsx | 1 - .../server/routes/fleet/get_apm_package_policy_definition.ts | 1 - x-pack/plugins/apm/server/routes/fleet/source_maps.test.ts | 1 - 4 files changed, 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.stories.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.stories.tsx index fd3ee2f3e455..bef88a78f684 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.stories.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.stories.tsx @@ -118,7 +118,6 @@ const policy = { namespace: 'default', policy_id: 'policy-elastic-agent-on-cloud', enabled: true, - output_id: '', inputs: [ { type: 'apm', @@ -341,7 +340,6 @@ const newPolicy = { namespace: 'default', policy_id: 'policy-elastic-agent-on-cloud', enabled: true, - output_id: '', package: { name: 'apm', title: 'Elastic APM', diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx index c1bfa7ac6cf6..b4243124766a 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx @@ -81,7 +81,6 @@ const policy = { namespace: 'default', enabled: true, policy_id: 'policy-elastic-agent-on-cloud', - output_id: '', package: { name: 'apm', version: '8.3.0', diff --git a/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts b/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts index 023ba2755ea9..d2eea5db4ef1 100644 --- a/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts +++ b/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts @@ -40,7 +40,6 @@ export async function getApmPackagePolicyDefinition({ namespace: 'default', enabled: true, policy_id: POLICY_ELASTIC_AGENT_ON_CLOUD, - output_id: '', inputs: [ { type: 'apm', diff --git a/x-pack/plugins/apm/server/routes/fleet/source_maps.test.ts b/x-pack/plugins/apm/server/routes/fleet/source_maps.test.ts index d4f9b79e516c..0f617b076ab8 100644 --- a/x-pack/plugins/apm/server/routes/fleet/source_maps.test.ts +++ b/x-pack/plugins/apm/server/routes/fleet/source_maps.test.ts @@ -19,7 +19,6 @@ const packagePolicy = { namespace: 'default', policy_id: '7a87c160-c961-11eb-81e2-f7327d61c92a', enabled: true, - output_id: '', inputs: [ { policy_template: 'apmserver', From 6f3613bccd263ab1e6081fe0810d7669c4bd319f Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 30 Sep 2022 10:15:09 -0500 Subject: [PATCH 163/185] skip flaky jest suite (#142312) --- .../indicators/hooks/use_aggregated_indicators.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx index f067df33d79d..7791020d58e0 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.test.tsx @@ -28,7 +28,8 @@ const renderUseAggregatedIndicators = () => wrapper: TestProvidersComponent, }); -describe('useAggregatedIndicators()', () => { +// FLAKY: https://github.com/elastic/kibana/issues/142312 +describe.skip('useAggregatedIndicators()', () => { beforeEach(jest.clearAllMocks); type MockedCreateFetchAggregatedIndicators = jest.MockedFunction< From 93b97bf5b2aece91bf56f620a84a197e3b055152 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Fri, 30 Sep 2022 17:50:01 +0200 Subject: [PATCH 164/185] [Lens] Add control for global filters to the annotation layer menu (#141615) * :sparkles: Add ignore global filters feature * :white_check_mark: Fix tests * :wrench: Remove unused translations * :recycle: Make it simpler * :white_check_mark: Fix test * :wrench: slighlty increase bundle limit size * :wrench: Forward layer flag to event config * :recycle: refactor actions code to include viz custom actions * :bug: Flip the logic * :fire: Remove unused file * :sparkles: Migrate ignore flag to annotation layers * :white_check_mark: Add unit test for default dataView --- .../lib/configurations/xy/layers.test.ts | 84 ++++++++++++++- .../lib/configurations/xy/layers.ts | 26 +++-- .../layer_actions/clone_layer_action.tsx | 4 +- .../layer_actions/layer_actions.tsx | 102 +++++++++--------- .../layer_actions/remove_layer_action.tsx | 5 +- .../config_panel/layer_actions/types.ts | 17 --- .../editor_frame/config_panel/layer_panel.tsx | 45 ++++++-- x-pack/plugins/lens/public/types.ts | 22 ++++ .../visualizations/xy/annotations/actions.ts | 54 ++++++++++ .../public/visualizations/xy/to_expression.ts | 5 +- .../visualizations/xy/visualization.test.ts | 77 +++++++++++++ .../visualizations/xy/visualization.tsx | 25 +++-- .../annotations_panel.tsx | 46 ++++---- .../annotations_config_panel/helpers.ts | 1 + 14 files changed, 387 insertions(+), 126 deletions(-) delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts index dd4926e7aeb7..6c94971397d3 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.test.ts @@ -309,6 +309,27 @@ describe('getLayers', () => { ], series: [createSeries({ metrics: staticValueMetric })], }); + const panelWithSingleAnnotationDefaultDataView = createPanel({ + annotations: [ + { + fields: 'geo.src,host', + template: 'Security Error from {{geo.src}} on {{host}}', + query_string: { + query: 'tags:error AND tags:security', + language: 'lucene', + }, + id: 'ann1', + color: 'rgba(211,49,21,0.7)', + time_field: 'timestamp', + icon: 'fa-asterisk', + ignore_global_filters: 1, + ignore_panel_filters: 1, + hidden: true, + index_pattern: '', + }, + ], + series: [createSeries({ metrics: staticValueMetric })], + }); test.each<[string, [Record, Panel], Array>]>([ [ @@ -521,6 +542,14 @@ describe('getLayers', () => { timeField: 'timestamp', type: 'query', }, + ], + indexPatternId: 'test', + }, + { + layerId: 'test-id', + layerType: 'annotations', + ignoreGlobalFilters: false, + annotations: [ { color: '#0000FF', filter: { @@ -567,6 +596,51 @@ describe('getLayers', () => { }, ], ], + [ + 'annotation layer gets correct dataView when none is defined', + [dataSourceLayersWithStatic, panelWithSingleAnnotationDefaultDataView], + [ + { + layerType: 'referenceLine', + accessors: ['column-id-1'], + layerId: 'test-layer-1', + yConfig: [ + { + forAccessor: 'column-id-1', + axisMode: 'right', + color: '#68BC00', + fill: 'below', + }, + ], + }, + { + layerId: 'test-id', + layerType: 'annotations', + ignoreGlobalFilters: true, + annotations: [ + { + color: '#D33115', + extraFields: ['geo.src'], + filter: { + language: 'lucene', + query: 'tags:error AND tags:security', + type: 'kibana_query', + }, + icon: 'asterisk', + id: 'ann1', + isHidden: true, + key: { + type: 'point_in_time', + }, + label: 'Event', + timeField: 'timestamp', + type: 'query', + }, + ], + indexPatternId: 'default', + }, + ], + ], ])('should return %s', async (_, input, expected) => { const layers = await getLayers(...input, indexPatternsService as DataViewsPublicPluginStart); expect(layers).toEqual(expected.map(expect.objectContaining)); @@ -583,8 +657,14 @@ const mockedIndices = [ ] as unknown as DataView[]; const indexPatternsService = { - getDefault: jest.fn(() => Promise.resolve({ id: 'default', title: 'index' })), - get: jest.fn(() => Promise.resolve(mockedIndices[0])), + getDefault: jest.fn(() => + Promise.resolve({ + id: 'default', + title: 'index', + getFieldByName: (name: string) => ({ aggregatable: name !== 'host' }), + }) + ), + get: jest.fn((id) => Promise.resolve({ ...mockedIndices[0], id })), find: jest.fn((search: string, size: number) => { if (size !== 1) { // shouldn't request more than one data view since there is a significant performance penalty diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts index 7cc2aea19cbd..ec0e24e2db87 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/xy/layers.ts @@ -63,7 +63,7 @@ function getColor( } function nonNullable(value: T): value is NonNullable { - return value !== null && value !== undefined; + return value != null; } export const getLayers = async ( @@ -132,16 +132,22 @@ export const getLayers = async ( return nonAnnotationsLayers; } - const annotationsByIndexPattern = groupBy( - model.annotations, - (a) => typeof a.index_pattern === 'object' && 'id' in a.index_pattern && a.index_pattern.id - ); + const annotationsByIndexPatternAndIgnoreFlag = groupBy(model.annotations, (a) => { + const id = typeof a.index_pattern === 'object' && 'id' in a.index_pattern && a.index_pattern.id; + return `${id}-${Boolean(a.ignore_global_filters)}`; + }); try { const annotationsLayers: Array = await Promise.all( - Object.entries(annotationsByIndexPattern).map(async ([indexPatternId, annotations]) => { + Object.values(annotationsByIndexPatternAndIgnoreFlag).map(async (annotations) => { + const [firstAnnotation] = annotations; + const indexPatternId = + typeof firstAnnotation.index_pattern === 'string' + ? firstAnnotation.index_pattern + : firstAnnotation.index_pattern?.id; const convertedAnnotations: EventAnnotationConfig[] = []; - const { indexPattern } = (await fetchIndexPattern({ id: indexPatternId }, dataViews)) || {}; + const { indexPattern } = + (await fetchIndexPattern(indexPatternId && { id: indexPatternId }, dataViews)) || {}; if (indexPattern) { annotations.forEach((a: Annotation) => { @@ -153,9 +159,9 @@ export const getLayers = async ( return { layerId: v4(), layerType: 'annotations', - ignoreGlobalFilters: true, + ignoreGlobalFilters: Boolean(firstAnnotation.ignore_global_filters), annotations: convertedAnnotations, - indexPatternId, + indexPatternId: indexPattern.id!, }; } }) @@ -173,7 +179,7 @@ const convertAnnotation = ( ): EventAnnotationConfig | undefined => { const extraFields = annotation.fields ?.replace(/\s/g, '') - ?.split(',') + .split(',') .map((field) => { const dataViewField = dataView.getFieldByName(field); return dataViewField && dataViewField.aggregatable ? field : undefined; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx index 26d4c1f04f41..97d0cf73b80d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx @@ -6,8 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { Visualization } from '../../../..'; -import { LayerAction } from './types'; +import type { LayerAction } from '../../../../types'; +import type { Visualization } from '../../../..'; interface CloneLayerAction { execute: () => void; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx index b9ca695882ef..bc1c41caa650 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import type { CoreStart } from '@kbn/core/public'; import { EuiButtonIcon, EuiContextMenuPanel, @@ -18,13 +17,28 @@ import { EuiText, EuiOutsideClickDetector, } from '@elastic/eui'; -import type { LayerType, Visualization } from '../../../..'; -import type { LayerAction } from './types'; - +import type { CoreStart } from '@kbn/core/public'; +import type { LayerType } from '../../../..'; +import type { LayerAction, Visualization } from '../../../../types'; import { getCloneLayerAction } from './clone_layer_action'; import { getRemoveLayerAction } from './remove_layer_action'; export interface LayerActionsProps { + layerIndex: number; + actions: LayerAction[]; +} + +/** @internal **/ +export const getSharedActions = ({ + core, + layerIndex, + layerType, + activeVisualization, + isOnlyLayer, + isTextBasedLanguage, + onCloneLayer, + onRemoveLayer, +}: { onRemoveLayer: () => void; onCloneLayer: () => void; layerIndex: number; @@ -33,14 +47,25 @@ export interface LayerActionsProps { layerType?: LayerType; isTextBasedLanguage?: boolean; core: Pick; -} +}) => [ + getCloneLayerAction({ + execute: onCloneLayer, + layerIndex, + activeVisualization, + isTextBasedLanguage, + }), + getRemoveLayerAction({ + execute: onRemoveLayer, + layerIndex, + activeVisualization, + layerType, + isOnlyLayer, + core, + }), +]; /** @internal **/ -const InContextMenuActions = ( - props: LayerActionsProps & { - actions: LayerAction[]; - } -) => { +const InContextMenuActions = (props: LayerActionsProps) => { const dataTestSubject = `lnsLayerSplitButton--${props.layerIndex}`; const [isPopoverOpen, setPopover] = useState(false); const splitButtonPopoverId = useGeneratedHtmlId({ @@ -105,47 +130,24 @@ const InContextMenuActions = ( }; export const LayerActions = (props: LayerActionsProps) => { - const compatibleActions = useMemo( - () => - [ - getCloneLayerAction({ - execute: props.onCloneLayer, - layerIndex: props.layerIndex, - activeVisualization: props.activeVisualization, - isTextBasedLanguage: props.isTextBasedLanguage, - }), - getRemoveLayerAction({ - execute: props.onRemoveLayer, - layerIndex: props.layerIndex, - activeVisualization: props.activeVisualization, - layerType: props.layerType, - isOnlyLayer: props.isOnlyLayer, - core: props.core, - }), - ].filter((i) => i.isCompatible), - [props] - ); - - if (!compatibleActions.length) { + if (!props.actions.length) { return null; } - if (compatibleActions.length > 1) { - return ; - } else { - const [{ displayName, execute, icon, color, 'data-test-subj': dataTestSubj }] = - compatibleActions; - - return ( - - ); + if (props.actions.length > 1) { + return ; } + const [{ displayName, execute, icon, color, 'data-test-subj': dataTestSubj }] = props.actions; + + return ( + + ); }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx index 32a18d153569..58a4248b5185 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx @@ -22,10 +22,9 @@ import { import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { LayerAction } from './types'; -import { Visualization } from '../../../../types'; +import type { LayerAction, Visualization } from '../../../../types'; import { LOCAL_STORAGE_LENS_KEY } from '../../../../settings_storage'; -import { LayerType, layerTypes } from '../../../..'; +import { type LayerType, layerTypes } from '../../../..'; interface RemoveLayerAction { execute: () => void; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts deleted file mode 100644 index 4614874777cc..000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts +++ /dev/null @@ -1,17 +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 { IconType, EuiButtonIconColor } from '@elastic/eui'; - -/** @internal **/ -export interface LayerAction { - displayName: string; - execute: () => void | Promise; - icon: IconType; - color?: EuiButtonIconColor; - isCompatible: boolean; - 'data-test-subj'?: string; -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index e40281fa1f3e..3d1068ebd521 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -43,6 +43,7 @@ import { selectDatasourceStates, } from '../../../state_management'; import { onDropForVisualization } from './buttons/drop_targets_utils'; +import { getSharedActions } from './layer_actions/layer_actions'; const initialActiveDimensionState = { isNew: false, @@ -310,6 +311,39 @@ export function LayerPanel( const [datasource] = Object.values(framePublicAPI.datasourceLayers); const isTextBasedLanguage = Boolean(datasource?.isTextBasedLanguage()); + const compatibleActions = useMemo( + () => + [ + ...(activeVisualization.getSupportedActionsForLayer?.( + layerId, + visualizationState, + updateVisualization + ) || []), + ...getSharedActions({ + activeVisualization, + core, + layerIndex, + layerType: activeVisualization.getLayerType(layerId, visualizationState), + isOnlyLayer, + isTextBasedLanguage, + onCloneLayer, + onRemoveLayer, + }), + ].filter((i) => i.isCompatible), + [ + activeVisualization, + core, + isOnlyLayer, + isTextBasedLanguage, + layerId, + layerIndex, + onCloneLayer, + onRemoveLayer, + updateVisualization, + visualizationState, + ] + ); + return ( <>
    @@ -332,16 +366,7 @@ export function LayerPanel( /> - + {(layerDatasource || activeVisualization.renderLayerPanel) && } diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 486ddef88ee9..2ccc393a0932 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -30,6 +30,7 @@ import type { IndexPatternAggRestrictions } from '@kbn/data-plugin/public'; import type { FieldSpec, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { FieldFormatParams } from '@kbn/field-formats-plugin/common'; import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types'; +import type { EuiButtonIconColor } from '@elastic/eui'; import type { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import type { DateRange, LayerType, SortingHint } from '../common'; import type { @@ -506,6 +507,17 @@ export interface DatasourceDataPanelProps { usedIndexPatterns?: string[]; } +/** @internal **/ +export interface LayerAction { + displayName: string; + description?: string; + execute: () => void | Promise; + icon: IconType; + color?: EuiButtonIconColor; + isCompatible: boolean; + 'data-test-subj'?: string; +} + interface SharedDimensionProps { /** Visualizations can restrict operations based on their own rules. * For example, limiting to only bucketed or only numeric operations. @@ -962,6 +974,16 @@ export interface Visualization { staticValue?: unknown; }>; }>; + /** + * returns a list of custom actions supported by the visualization layer. + * Default actions like delete/clear are not included in this list and are managed by the editor frame + * */ + getSupportedActionsForLayer?: ( + layerId: string, + state: T, + setState: StateSetter + ) => LayerAction[]; + /** returns the type string of the given layer */ getLayerType: (layerId: string, state?: T) => LayerType | undefined; /* returns the type of removal operation to perform for the specific layer in the current state */ getRemoveOperation?: (state: T, layerId: string) => 'remove' | 'clear'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts new file mode 100644 index 000000000000..d7d3c9ca8a56 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { LayerAction, StateSetter } from '../../../types'; +import type { XYState, XYAnnotationLayerConfig } from '../types'; + +export const createAnnotationActions = ({ + state, + layer, + layerIndex, + setState, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; +}): LayerAction[] => { + const label = !layer.ignoreGlobalFilters + ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { + defaultMessage: 'Ignore global filters', + }) + : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersLabel', { + defaultMessage: 'Keep global filters', + }); + return [ + { + displayName: label, + description: !layer.ignoreGlobalFilters + ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { + defaultMessage: + 'All the dimensions configured in this layer ignore filters defined at kibana level.', + }) + : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersDescription', { + defaultMessage: + 'All the dimensions configured in this layer respect filters defined at kibana level.', + }), + execute: () => { + const newLayers = [...state.layers]; + newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; + return setState({ ...state, layers: newLayers }); + }, + icon: !layer.ignoreGlobalFilters ? 'eyeClosed' : 'eye', + isCompatible: true, + 'data-test-subj': !layer.ignoreGlobalFilters + ? 'lnsXY_annotationLayer_ignoreFilters' + : 'lnsXY_annotationLayer_keepFilters', + }, + ]; +}; 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 345e8ffcb5b1..bf75018f111e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -190,7 +190,10 @@ export const buildExpression = ( annotations: layer.annotations.map((c) => ({ ...c, label: uniqueLabels[c.id], - ignoreGlobalFilters: layer.ignoreGlobalFilters, + ...(c.type === 'query' + ? // Move the ignore flag at the event level + { ignoreGlobalFilters: layer.ignoreGlobalFilters } + : {}), })), }; }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index c62d2c1195e5..e76633b34992 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -2857,4 +2857,81 @@ describe('xy_visualization', () => { }); }); }); + + describe('getSupportedActionsForLayer', () => { + it('should return no actions for a data layer', () => { + expect( + xyVisualization.getSupportedActionsForLayer?.('first', exampleState(), jest.fn()) + ).toHaveLength(0); + }); + + it('should return one action for an annotation layer', () => { + const baseState = exampleState(); + expect( + xyVisualization.getSupportedActionsForLayer?.( + 'annotation', + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }, + jest.fn() + ) + ).toEqual([ + expect.objectContaining({ + displayName: 'Keep global filters', + description: + 'All the dimensions configured in this layer respect filters defined at kibana level.', + icon: 'eye', + isCompatible: true, + 'data-test-subj': 'lnsXY_annotationLayer_keepFilters', + }), + ]); + }); + + it('should return an action that performs a state update on click', () => { + const baseState = exampleState(); + const setState = jest.fn(); + const [action] = xyVisualization.getSupportedActionsForLayer?.( + 'annotation', + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }, + setState + )!; + action.execute(); + + expect(setState).toHaveBeenCalledWith( + expect.objectContaining({ + layers: expect.arrayContaining([ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: false, + indexPatternId: 'myIndexPattern', + }, + ]), + }) + ); + }); + }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index a5ccfc02b0fa..c013f0cd1d07 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -32,13 +32,13 @@ import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader, LayerHeaderContent } from './xy_config_panel/layer_header'; import { Visualization, AccessorConfig, FramePublicAPI } from '../../types'; import { - State, + type State, + type XYLayerConfig, + type XYDataLayerConfig, + type SeriesType, + type XYSuggestion, + type PersistedState, visualizationTypes, - XYLayerConfig, - XYDataLayerConfig, - SeriesType, - XYSuggestion, - PersistedState, } from './types'; import { layerTypes } from '../../../common'; import { @@ -84,12 +84,13 @@ import { validateLayersForDimension, } from './visualization_helpers'; import { groupAxesByType } from './axes_configuration'; -import { XYState } from './types'; +import type { XYState } from './types'; import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_panel'; import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; +import { createAnnotationActions } from './annotations/actions'; const XY_ID = 'lnsXY'; export const getXyVisualization = ({ @@ -240,6 +241,16 @@ export const getXyVisualization = ({ ]; }, + getSupportedActionsForLayer(layerId, state, setState) { + const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[layerIndex]; + const actions = []; + if (isAnnotationsLayer(layer)) { + actions.push(...createAnnotationActions({ state, layerIndex, layer, setState })); + } + return actions; + }, + onIndexPatternChange(state, indexPatternId, layerId) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index a6e1cd6b2ac3..480c0773f452 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -352,7 +352,11 @@ export const AnnotationsPanel = ( defaultMessage: 'Color', })} /> - setAnnotations({ isHidden: ev.target.checked })} /> @@ -384,31 +388,25 @@ export const AnnotationsPanel = ( ); }; -const ConfigPanelHideSwitch = ({ +const ConfigPanelGenericSwitch = ({ + label, + ['data-test-subj']: dataTestSubj, value, onChange, }: { + label: string; + 'data-test-subj': string; value: boolean; onChange: (event: EuiSwitchEvent) => void; -}) => { - return ( - - - - ); -}; +}) => ( + + + +); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts index 2ca0f9530bbe..89fbdfd38fcf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts @@ -97,6 +97,7 @@ export const sanitizeProperties = (annotation: EventAnnotationConfig) => { 'textField', 'filter', 'extraFields', + 'ignoreGlobalFilters', ]); return lineAnnotation; } From f415b3b98f4c5c1c60447f9d907b94a5ceacd6a9 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Fri, 30 Sep 2022 09:17:56 -0700 Subject: [PATCH 165/185] [Security Solution][Exceptions] - Make exceptions read only when displaying deleted rule details (#142258) ### Summary Addresses 141899. --- .../components/all_exception_items_table/index.test.tsx | 6 ++++++ .../components/all_exception_items_table/index.tsx | 7 +++++-- .../detection_engine/rules/all/exceptions/translations.ts | 2 +- .../pages/detection_engine/rules/details/index.tsx | 2 ++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx index d34cf07aa914..139d171088ec 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.test.tsx @@ -109,6 +109,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.DETECTION} + isViewReadOnly={false} /> ); @@ -146,6 +147,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.DETECTION} + isViewReadOnly={false} /> ); @@ -183,6 +185,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.ENDPOINT} + isViewReadOnly={false} /> ); @@ -226,6 +229,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.DETECTION} + isViewReadOnly={false} /> ); @@ -268,6 +272,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.DETECTION} + isViewReadOnly={false} /> ); @@ -301,6 +306,7 @@ describe('ExceptionsViewer', () => { ], }} listType={ExceptionListTypeEnum.DETECTION} + isViewReadOnly={false} /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx index d090f5a3bc59..ffe07bb2c5df 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/index.tsx @@ -75,12 +75,15 @@ export interface GetExceptionItemProps { interface ExceptionsViewerProps { rule: Rule | null; listType: ExceptionListTypeEnum; + /* Used for when displaying exceptions for a rule that has since been deleted, forcing read only view */ + isViewReadOnly: boolean; onRuleChange?: () => void; } const ExceptionsViewerComponent = ({ rule, listType, + isViewReadOnly, onRuleChange, }: ExceptionsViewerProps): JSX.Element => { const { services } = useKibana(); @@ -337,8 +340,8 @@ const ExceptionsViewerComponent = ({ // User privileges checks useEffect((): void => { - setReadOnly(!canUserCRUD || !hasIndexWrite); - }, [setReadOnly, canUserCRUD, hasIndexWrite]); + setReadOnly(isViewReadOnly || !canUserCRUD || !hasIndexWrite); + }, [setReadOnly, isViewReadOnly, canUserCRUD, hasIndexWrite]); useEffect(() => { if (exceptionListsToQuery.length > 0) { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts index 8baf21db15c0..3d817adb2605 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts @@ -157,7 +157,7 @@ export const referenceErrorMessage = (referenceCount: number) => export const EXCEPTION_LIST_SEARCH_PLACEHOLDER = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.all.exceptions.searchPlaceholder', { - defaultMessage: 'Search by name or list_id', + defaultMessage: 'Search by name or list id', } ); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 5149269c57d9..0393fcf239f7 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -871,6 +871,7 @@ const RuleDetailsPageComponent: React.FC = ({ rule={rule} listType={ExceptionListTypeEnum.DETECTION} onRuleChange={refreshRule} + isViewReadOnly={!isExistingRule} data-test-subj="exceptionTab" /> @@ -881,6 +882,7 @@ const RuleDetailsPageComponent: React.FC = ({ rule={rule} listType={ExceptionListTypeEnum.ENDPOINT} onRuleChange={refreshRule} + isViewReadOnly={!isExistingRule} data-test-subj="endpointExceptionsTab" /> From e7c29831735f8b7abbc7670c191c0b4d4be16d90 Mon Sep 17 00:00:00 2001 From: "Quynh Nguyen (Quinn)" <43350163+qn895@users.noreply.github.com> Date: Fri, 30 Sep 2022 11:33:56 -0500 Subject: [PATCH 166/185] [ML] Fix date picker not allowing unpause when refresh interval is 0 (#142005) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/aiops/kibana.json | 2 +- .../date_picker_wrapper.tsx | 237 +++++++++++++----- .../public/hooks/use_aiops_app_context.ts | 2 + .../date_picker_wrapper.tsx | 215 ++++++++++++---- .../hooks/use_time_filter.ts | 21 ++ .../aiops/explain_log_rate_spikes.tsx | 1 + .../application/aiops/log_categorization.tsx | 1 + .../date_picker_wrapper.test.tsx | 7 +- .../date_picker_wrapper.tsx | 18 +- 9 files changed, 380 insertions(+), 124 deletions(-) diff --git a/x-pack/plugins/aiops/kibana.json b/x-pack/plugins/aiops/kibana.json index 6648816b0784..ce8057bc03f0 100755 --- a/x-pack/plugins/aiops/kibana.json +++ b/x-pack/plugins/aiops/kibana.json @@ -15,6 +15,6 @@ "licensing" ], "optionalPlugins": [], - "requiredBundles": ["fieldFormats"], + "requiredBundles": ["fieldFormats", "kibanaReact"], "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx index 605f130594bc..6b3e57200b90 100644 --- a/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx @@ -7,19 +7,33 @@ // TODO Consolidate with duplicate component `DatePickerWrapper` in // `x-pack/plugins/data_visualizer/public/application/common/components/date_picker_wrapper/date_picker_wrapper.tsx` - +// `x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx` import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { Subscription } from 'rxjs'; import { debounce } from 'lodash'; -import { EuiSuperDatePicker, OnRefreshProps } from '@elastic/eui'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiSuperDatePicker, + OnRefreshProps, + OnTimeChangeProps, +} from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; -import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public'; +import { TimefilterContract, TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs/operators'; +import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; import { useUrlState } from '../../hooks/use_url_state'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { aiopsRefresh$ } from '../../application/services/timefilter_refresh_service'; +const DEFAULT_REFRESH_INTERVAL_MS = 5000; + interface TimePickerQuickRange { from: string; to: string; @@ -49,19 +63,64 @@ function getRecentlyUsedRangesFactory(timeHistory: TimeHistoryContract) { }; } -function updateLastRefresh(timeRange: OnRefreshProps) { +function updateLastRefresh(timeRange?: OnRefreshProps) { aiopsRefresh$.next({ lastRefresh: Date.now(), timeRange }); } +export const useRefreshIntervalUpdates = (timefilter: TimefilterContract) => { + return useObservable( + timefilter.getRefreshIntervalUpdate$().pipe(map(timefilter.getRefreshInterval)), + timefilter.getRefreshInterval() + ); +}; + +export const useTimeRangeUpdates = (timefilter: TimefilterContract, absolute = false) => { + const getTimeCallback = absolute + ? timefilter.getAbsoluteTime.bind(timefilter) + : timefilter.getTime.bind(timefilter); + + return useObservable(timefilter.getTimeUpdate$().pipe(map(getTimeCallback)), getTimeCallback()); +}; + export const DatePickerWrapper: FC = () => { - const { uiSettings, data } = useAiopsAppContext(); - const { timefilter, history } = data.query.timefilter; + const services = useAiopsAppContext(); + const { toasts } = services.notifications; + const config = services.uiSettings; + + const { timefilter, history } = services.data.query.timefilter; + const theme$ = services.theme.theme$; const [globalState, setGlobalState] = useUrlState('_g'); const getRecentlyUsedRanges = getRecentlyUsedRangesFactory(history); - const refreshInterval: RefreshInterval = - globalState?.refreshInterval ?? timefilter.getRefreshInterval(); + const timeFilterRefreshInterval = useRefreshIntervalUpdates(timefilter); + const time = useTimeRangeUpdates(timefilter); + + useEffect( + function syncTimRangeFromUrlState() { + if (globalState?.time !== undefined) { + timefilter.setTime({ + from: globalState.time.from, + to: globalState.time.to, + }); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [globalState?.time?.from, globalState?.time?.to, globalState?.time?.ts] + ); + + useEffect( + function syncRefreshIntervalFromUrlState() { + if (globalState?.refreshInterval !== undefined) { + timefilter.setRefreshInterval({ + pause: !!globalState?.refreshInterval?.pause, + value: globalState?.refreshInterval?.value, + }); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [globalState?.refreshInterval] + ); // eslint-disable-next-line react-hooks/exhaustive-deps const setRefreshInterval = useCallback( @@ -71,7 +130,6 @@ export const DatePickerWrapper: FC = () => { [setGlobalState] ); - const [time, setTime] = useState(timefilter.getTime()); const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges()); const [isAutoRefreshSelectorEnabled, setIsAutoRefreshSelectorEnabled] = useState( timefilter.isAutoRefreshSelectorEnabled() @@ -80,8 +138,69 @@ export const DatePickerWrapper: FC = () => { timefilter.isTimeRangeSelectorEnabled() ); - const dateFormat = uiSettings.get('dateFormat'); - const timePickerQuickRanges = uiSettings.get( + const refreshInterval = useMemo( + (): RefreshInterval => globalState?.refreshInterval ?? timeFilterRefreshInterval, + // eslint-disable-next-line react-hooks/exhaustive-deps + [JSON.stringify(globalState?.refreshInterval), timeFilterRefreshInterval] + ); + + useEffect( + function warnAboutShortRefreshInterval() { + const isResolvedFromUrlState = !!globalState?.refreshInterval; + const isTooShort = refreshInterval.value < DEFAULT_REFRESH_INTERVAL_MS; + + // Only warn about short interval with enabled auto-refresh. + if (!isTooShort || refreshInterval.pause) return; + + toasts.addWarning( + { + title: isResolvedFromUrlState + ? i18n.translate('xpack.aiops.datePicker.shortRefreshIntervalURLWarningMessage', { + defaultMessage: + 'The refresh interval in the URL is shorter than the minimum supported by Machine Learning.', + }) + : i18n.translate( + 'xpack.aiops.datePicker.shortRefreshIntervalTimeFilterWarningMessage', + { + defaultMessage: + 'The refresh interval in Advanced Settings is shorter than the minimum supported by Machine Learning.', + } + ), + text: toMountPoint( + wrapWithTheme( + + + , + theme$ + ) + ), + }, + { toastLifeTimeMs: 30000 } + ); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + // eslint-disable-next-line react-hooks/exhaustive-deps + JSON.stringify(refreshInterval), + // eslint-disable-next-line react-hooks/exhaustive-deps + JSON.stringify(globalState?.refreshInterval), + setRefreshInterval, + ] + ); + + const dateFormat = config.get('dateFormat'); + const timePickerQuickRanges = config.get( UI_SETTINGS.TIMEPICKER_QUICK_RANGES ); @@ -97,22 +216,7 @@ export const DatePickerWrapper: FC = () => { useEffect(() => { const subscriptions = new Subscription(); - const refreshIntervalUpdate$ = timefilter.getRefreshIntervalUpdate$(); - if (refreshIntervalUpdate$ !== undefined) { - subscriptions.add( - refreshIntervalUpdate$.subscribe((r) => { - setRefreshInterval(timefilter.getRefreshInterval()); - }) - ); - } - const timeUpdate$ = timefilter.getTimeUpdate$(); - if (timeUpdate$ !== undefined) { - subscriptions.add( - timeUpdate$.subscribe((v) => { - setTime(timefilter.getTime()); - }) - ); - } + const enabledUpdated$ = timefilter.getEnabledUpdated$(); if (enabledUpdated$ !== undefined) { subscriptions.add( @@ -126,15 +230,21 @@ export const DatePickerWrapper: FC = () => { return function cleanup() { subscriptions.unsubscribe(); }; - }, [setRefreshInterval, timefilter]); - - function updateFilter({ start, end }: Duration) { - const newTime = { from: start, to: end }; - // Update timefilter for controllers listening for changes - timefilter.setTime(newTime); - setTime(newTime); - setRecentlyUsedRanges(getRecentlyUsedRanges()); - } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const updateTimeFilter = useCallback( + ({ start, end }: OnTimeChangeProps) => { + setRecentlyUsedRanges(getRecentlyUsedRanges()); + setGlobalState('time', { + from: start, + to: end, + ...(start === 'now' || end === 'now' ? { ts: Date.now() } : {}), + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [setGlobalState] + ); function updateInterval({ isPaused: pause, @@ -146,26 +256,41 @@ export const DatePickerWrapper: FC = () => { setRefreshInterval({ pause, value }); } - /** - * Enforce pause when it's set to false with 0 refresh interval. - */ - const isPaused = refreshInterval.pause || (!refreshInterval.pause && !refreshInterval.value); - return isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled ? ( -
    - -
    + + + + + + {isTimeRangeSelectorEnabled ? null : ( + + updateLastRefresh()} + data-test-subj="aiOpsRefreshPageButton" + > + + + + )} + ) : null; }; diff --git a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts index 3c44ae7bdb0a..ddbfca3eb8b1 100644 --- a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts +++ b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts @@ -15,6 +15,7 @@ import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { CoreStart, CoreSetup, HttpStart, IUiSettingsClient } from '@kbn/core/public'; +import type { ThemeServiceStart } from '@kbn/core/public'; export interface AiopsAppDependencies { application: CoreStart['application']; @@ -24,6 +25,7 @@ export interface AiopsAppDependencies { http: HttpStart; notifications: CoreSetup['notifications']; storage: IStorageWrapper; + theme: ThemeServiceStart; uiSettings: IUiSettingsClient; unifiedSearch: UnifiedSearchPublicPluginStart; share: SharePluginStart; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/date_picker_wrapper/date_picker_wrapper.tsx index 7130841d0a6a..12d6b257ec29 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/date_picker_wrapper/date_picker_wrapper.tsx @@ -5,17 +5,35 @@ * 2.0. */ +// TODO Consolidate with duplicate component `DatePickerWrapper` in +// `x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx` + import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { Subscription } from 'rxjs'; import { debounce } from 'lodash'; -import { EuiSuperDatePicker, OnRefreshProps } from '@elastic/eui'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiSuperDatePicker, + OnRefreshProps, + OnTimeChangeProps, +} from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public'; - -import { useUrlState } from '../../util/url_state'; +import { i18n } from '@kbn/i18n'; +import { wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + useRefreshIntervalUpdates, + useTimeRangeUpdates, +} from '../../../index_data_visualizer/hooks/use_time_filter'; import { useDataVisualizerKibana } from '../../../kibana_context'; import { dataVisualizerRefresh$ } from '../../../index_data_visualizer/services/timefilter_refresh_service'; +import { useUrlState } from '../../util/url_state'; + +const DEFAULT_REFRESH_INTERVAL_MS = 5000; interface TimePickerQuickRange { from: string; @@ -46,20 +64,52 @@ function getRecentlyUsedRangesFactory(timeHistory: TimeHistoryContract) { }; } -function updateLastRefresh(timeRange: OnRefreshProps) { +function updateLastRefresh(timeRange?: OnRefreshProps) { dataVisualizerRefresh$.next({ lastRefresh: Date.now(), timeRange }); } +// FIXME: Consolidate this component with ML and AIOps's component export const DatePickerWrapper: FC = () => { - const { services } = useDataVisualizerKibana(); + const { + services, + notifications: { toasts }, + } = useDataVisualizerKibana(); const config = services.uiSettings; + const theme$ = services.theme.theme$; + const { timefilter, history } = services.data.query.timefilter; const [globalState, setGlobalState] = useUrlState('_g'); const getRecentlyUsedRanges = getRecentlyUsedRangesFactory(history); - const refreshInterval: RefreshInterval = - globalState?.refreshInterval ?? timefilter.getRefreshInterval(); + const timeFilterRefreshInterval = useRefreshIntervalUpdates(); + const time = useTimeRangeUpdates(); + + useEffect( + function syncTimRangeFromUrlState() { + if (globalState?.time !== undefined) { + timefilter.setTime({ + from: globalState.time.from, + to: globalState.time.to, + }); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [globalState?.time?.from, globalState?.time?.to, globalState?.time?.ts] + ); + + useEffect( + function syncRefreshIntervalFromUrlState() { + if (globalState?.refreshInterval !== undefined) { + timefilter.setRefreshInterval({ + pause: !!globalState?.refreshInterval?.pause, + value: globalState?.refreshInterval?.value, + }); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [globalState?.refreshInterval] + ); // eslint-disable-next-line react-hooks/exhaustive-deps const setRefreshInterval = useCallback( @@ -69,7 +119,6 @@ export const DatePickerWrapper: FC = () => { [setGlobalState] ); - const [time, setTime] = useState(timefilter.getTime()); const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges()); const [isAutoRefreshSelectorEnabled, setIsAutoRefreshSelectorEnabled] = useState( timefilter.isAutoRefreshSelectorEnabled() @@ -78,6 +127,57 @@ export const DatePickerWrapper: FC = () => { timefilter.isTimeRangeSelectorEnabled() ); + const refreshInterval = useMemo( + (): RefreshInterval => globalState?.refreshInterval ?? timeFilterRefreshInterval, + // eslint-disable-next-line react-hooks/exhaustive-deps + [JSON.stringify(globalState?.refreshInterval), timeFilterRefreshInterval] + ); + + useEffect( + function warnAboutShortRefreshInterval() { + const isTooShort = refreshInterval.value < DEFAULT_REFRESH_INTERVAL_MS; + + // Only warn about short interval with enabled auto-refresh. + if (!isTooShort || refreshInterval.pause) return; + + toasts.warning({ + title: i18n.translate( + 'xpack.dataVisualizer.index.datePicker.shortRefreshIntervalURLWarningMessage', + { + defaultMessage: + 'The refresh interval in the URL is shorter than the minimum supported by Machine Learning.', + } + ), + body: wrapWithTheme( + + + , + theme$ + ), + toastLifeTimeMs: 30000, + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + // eslint-disable-next-line react-hooks/exhaustive-deps + JSON.stringify(refreshInterval), + // eslint-disable-next-line react-hooks/exhaustive-deps + JSON.stringify(globalState?.refreshInterval), + setRefreshInterval, + ] + ); + const dateFormat = config.get('dateFormat'); const timePickerQuickRanges = config.get( UI_SETTINGS.TIMEPICKER_QUICK_RANGES @@ -95,22 +195,7 @@ export const DatePickerWrapper: FC = () => { useEffect(() => { const subscriptions = new Subscription(); - const refreshIntervalUpdate$ = timefilter.getRefreshIntervalUpdate$(); - if (refreshIntervalUpdate$ !== undefined) { - subscriptions.add( - refreshIntervalUpdate$.subscribe((r) => { - setRefreshInterval(timefilter.getRefreshInterval()); - }) - ); - } - const timeUpdate$ = timefilter.getTimeUpdate$(); - if (timeUpdate$ !== undefined) { - subscriptions.add( - timeUpdate$.subscribe((v) => { - setTime(timefilter.getTime()); - }) - ); - } + const enabledUpdated$ = timefilter.getEnabledUpdated$(); if (enabledUpdated$ !== undefined) { subscriptions.add( @@ -124,15 +209,21 @@ export const DatePickerWrapper: FC = () => { return function cleanup() { subscriptions.unsubscribe(); }; - }, [setRefreshInterval, timefilter]); - - function updateFilter({ start, end }: Duration) { - const newTime = { from: start, to: end }; - // Update timefilter for controllers listening for changes - timefilter.setTime(newTime); - setTime(newTime); - setRecentlyUsedRanges(getRecentlyUsedRanges()); - } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const updateTimeFilter = useCallback( + ({ start, end }: OnTimeChangeProps) => { + setRecentlyUsedRanges(getRecentlyUsedRanges()); + setGlobalState('time', { + from: start, + to: end, + ...(start === 'now' || end === 'now' ? { ts: Date.now() } : {}), + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [setGlobalState] + ); function updateInterval({ isPaused: pause, @@ -144,26 +235,44 @@ export const DatePickerWrapper: FC = () => { setRefreshInterval({ pause, value }); } - /** - * Enforce pause when it's set to false with 0 refresh interval. - */ - const isPaused = refreshInterval.pause || (!refreshInterval.pause && !refreshInterval.value); - return isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled ? ( -
    - -
    + + + + + + {isTimeRangeSelectorEnabled ? null : ( + + updateLastRefresh()} + data-test-subj="dataVisualizerRefreshPageButton" + > + + + + )} + ) : null; }; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_time_filter.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_time_filter.ts index 132d03c81c0e..727c8bab88dc 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_time_filter.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_time_filter.ts @@ -6,6 +6,8 @@ */ import { useEffect } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs/operators'; import { useDataVisualizerKibana } from '../../kibana_context'; interface UseTimefilterOptions { @@ -36,3 +38,22 @@ export const useTimefilter = ({ return timefilter; }; + +export const useRefreshIntervalUpdates = () => { + const timefilter = useTimefilter(); + + return useObservable( + timefilter.getRefreshIntervalUpdate$().pipe(map(timefilter.getRefreshInterval)), + timefilter.getRefreshInterval() + ); +}; + +export const useTimeRangeUpdates = (absolute = false) => { + const timefilter = useTimefilter(); + + const getTimeCallback = absolute + ? timefilter.getAbsoluteTime.bind(timefilter) + : timefilter.getTime.bind(timefilter); + + return useObservable(timefilter.getTimeUpdate$().pipe(map(getTimeCallback)), getTimeCallback()); +}; diff --git a/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx b/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx index 2a8ce6e04144..9cfa17fe7194 100644 --- a/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx +++ b/x-pack/plugins/ml/public/application/aiops/explain_log_rate_spikes.tsx @@ -57,6 +57,7 @@ export const ExplainLogRateSpikesPage: FC = () => { 'storage', 'uiSettings', 'unifiedSearch', + 'theme', ])} /> )} diff --git a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx index 899006b5918d..3f70e0c58324 100644 --- a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx +++ b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx @@ -57,6 +57,7 @@ export const LogCategorizationPage: FC = () => { 'storage', 'uiSettings', 'unifiedSearch', + 'theme', ])} /> )} diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx index 5f263b51364e..536cc26888a9 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx @@ -123,7 +123,7 @@ describe('Navigation Menu: ', () => { refreshSubscription.unsubscribe(); }); - test('should not allow disabled pause with 0 refresh interval', () => { + test('should set interval to default of 5s when pause is disabled and refresh interval is 0', () => { // arrange (useUrlState as jest.Mock).mockReturnValue([{ refreshInterval: { pause: false, value: 0 } }]); @@ -137,9 +137,10 @@ describe('Navigation Menu: ', () => { render(); // assert - expect(displayWarningSpy).not.toHaveBeenCalled(); + // Show warning that the interval set is too short + expect(displayWarningSpy).toHaveBeenCalled(); const calledWith = MockedEuiSuperDatePicker.mock.calls[0][0]; - expect(calledWith.isPaused).toBe(true); + expect(calledWith.isPaused).toBe(false); expect(calledWith.refreshInterval).toBe(5000); }); diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx index 75cb787abadc..75c503a13949 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx @@ -126,18 +126,11 @@ export const DatePickerWrapper: FC = () => { timefilter.isTimeRangeSelectorEnabled() ); - const refreshInterval = useMemo((): RefreshInterval => { - const resultInterval = globalState?.refreshInterval ?? timeFilterRefreshInterval; - - /** - * Enforce pause when it's set to false with 0 refresh interval. - */ - const pause = resultInterval.pause || (!resultInterval.pause && resultInterval.value <= 0); - const value = resultInterval.value; - - return { value, pause }; + const refreshInterval = useMemo( + (): RefreshInterval => globalState?.refreshInterval ?? timeFilterRefreshInterval, // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(globalState?.refreshInterval), timeFilterRefreshInterval]); + [JSON.stringify(globalState?.refreshInterval), timeFilterRefreshInterval] + ); useEffect( function warnAboutShortRefreshInterval() { @@ -251,6 +244,9 @@ export const DatePickerWrapper: FC = () => { isPaused: boolean; refreshInterval: number; }) { + if (pause === false && value <= 0) { + setRefreshInterval({ pause, value: 5000 }); + } setRefreshInterval({ pause, value }); } From 874c93c3296a1c53d0ec04a76d8eaf872b758f20 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Fri, 30 Sep 2022 18:46:03 +0200 Subject: [PATCH 167/185] [ML] Show an info callout for new notifications (#142245) * storage context callbacks * ml notifications context * ml notifications context usage * info callout * update icon * add unit tests --- x-pack/plugins/ml/public/application/app.tsx | 5 +- .../ml_page/notifications_indicator.tsx | 56 +------ .../ml/ml_notifications_context.test.tsx | 140 ++++++++++++++++++ .../contexts/ml/ml_notifications_context.tsx | 91 ++++++++++++ .../contexts/storage/storage_context.tsx | 23 +-- .../components/notifications_list.tsx | 25 +++- .../ml/public/application/routing/router.tsx | 10 +- 7 files changed, 279 insertions(+), 71 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.test.tsx create mode 100644 x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.tsx diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 8d5958c2f557..1014b4e3a08b 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -15,6 +15,7 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { MlStorageContextProvider } from './contexts/storage'; import { setDependencyCache, clearCache } from './util/dependency_cache'; import { setLicenseCache } from './license'; import type { MlSetupDependencies, MlStartDependencies } from '../plugin'; @@ -109,7 +110,9 @@ const App: FC = ({ coreStart, deps, appMountParams }) => { mlServices: getMlGlobalServices(coreStart.http, deps.usageCollection), }} > - + + + diff --git a/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx b/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx index d0e3516af3db..2d95f0c97169 100644 --- a/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_page/notifications_indicator.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { @@ -15,60 +15,14 @@ import { EuiNotificationBadge, EuiToolTip, } from '@elastic/eui'; -import { combineLatest, of, timer } from 'rxjs'; -import { catchError, switchMap } from 'rxjs/operators'; -import moment from 'moment'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; -import { useAsObservable } from '../../hooks'; -import { NotificationsCountResponse } from '../../../../common/types/notifications'; -import { useMlKibana } from '../../contexts/kibana'; -import { useStorage } from '../../contexts/storage'; -import { ML_NOTIFICATIONS_LAST_CHECKED_AT } from '../../../../common/types/storage'; - -const NOTIFICATIONS_CHECK_INTERVAL = 60000; +import { useMlNotifications } from '../../contexts/ml/ml_notifications_context'; export const NotificationsIndicator: FC = () => { - const { - services: { - mlServices: { mlApiServices }, - }, - } = useMlKibana(); + const { notificationsCounts, latestRequestedAt } = useMlNotifications(); const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); - const [lastCheckedAt] = useStorage(ML_NOTIFICATIONS_LAST_CHECKED_AT); - const lastCheckedAt$ = useAsObservable(lastCheckedAt); - - /** Holds the value used for the actual request */ - const [lastCheckRequested, setLastCheckRequested] = useState(); - const [notificationsCounts, setNotificationsCounts] = useState(); - - useEffect(function startPollingNotifications() { - const subscription = combineLatest([lastCheckedAt$, timer(0, NOTIFICATIONS_CHECK_INTERVAL)]) - .pipe( - switchMap(([lastChecked]) => { - const lastCheckedAtQuery = lastChecked ?? moment().subtract(7, 'd').valueOf(); - setLastCheckRequested(lastCheckedAtQuery); - // Use the latest check time or 7 days ago by default. - return mlApiServices.notifications.countMessages$({ - lastCheckedAt: lastCheckedAtQuery, - }); - }), - catchError((error) => { - // Fail silently for now - return of({} as NotificationsCountResponse); - }) - ) - .subscribe((response) => { - setNotificationsCounts(response); - }); - - return () => { - subscription.unsubscribe(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const errorsAndWarningCount = (notificationsCounts?.error ?? 0) + (notificationsCounts?.warning ?? 0); const hasUnread = notificationsCounts && Object.values(notificationsCounts).some((v) => v > 0); @@ -91,7 +45,7 @@ export const NotificationsIndicator: FC = () => { defaultMessage="There {count, plural, one {is # notification} other {are # notifications}} with error or warning level since {lastCheckedAt}" values={{ count: errorsAndWarningCount, - lastCheckedAt: dateFormatter(lastCheckRequested), + lastCheckedAt: dateFormatter(latestRequestedAt), }} /> } @@ -115,7 +69,7 @@ export const NotificationsIndicator: FC = () => { } > diff --git a/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.test.tsx b/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.test.tsx new file mode 100644 index 000000000000..4076966942cd --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.test.tsx @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { of } from 'rxjs'; +import { useMlNotifications, MlNotificationsContextProvider } from './ml_notifications_context'; +import { useStorage } from '../storage'; + +const mockCountMessages = jest.fn(() => { + return of({ info: 1, error: 0, warning: 0 }); +}); + +jest.mock('../kibana', () => ({ + useMlKibana: () => { + return { + services: { + mlServices: { + mlApiServices: { + notifications: { + countMessages$: mockCountMessages, + }, + }, + }, + }, + }; + }, +})); + +const mockSetStorageValue = jest.fn(); +jest.mock('../storage', () => ({ + useStorage: jest.fn(() => { + return [undefined, mockSetStorageValue]; + }), +})); + +describe('useMlNotifications', () => { + beforeEach(() => { + jest.useFakeTimers('modern'); + jest.setSystemTime(1663945337063); + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.clearAllTimers(); + jest.useRealTimers(); + }); + + test('returns the default values', () => { + const { result } = renderHook(useMlNotifications, { wrapper: MlNotificationsContextProvider }); + expect(result.current.notificationsCounts).toEqual({ info: 0, error: 0, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(null); + expect(result.current.lastCheckedAt).toEqual(undefined); + }); + + test('starts polling for notifications with a 1 minute interval during the last week by default ', () => { + const { result } = renderHook(useMlNotifications, { + wrapper: MlNotificationsContextProvider, + }); + + act(() => { + jest.advanceTimersByTime(0); + }); + + expect(mockCountMessages).toHaveBeenCalledTimes(1); + expect(mockCountMessages).toHaveBeenCalledWith({ lastCheckedAt: 1663340537063 }); + expect(result.current.notificationsCounts).toEqual({ info: 1, error: 0, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(1663340537063); + expect(result.current.lastCheckedAt).toEqual(undefined); + + act(() => { + mockCountMessages.mockReturnValueOnce(of({ info: 1, error: 2, warning: 0 })); + jest.advanceTimersByTime(60000); + }); + + expect(mockCountMessages).toHaveBeenCalledTimes(2); + expect(mockCountMessages).toHaveBeenCalledWith({ lastCheckedAt: 1663340537063 + 60000 }); + expect(result.current.notificationsCounts).toEqual({ info: 1, error: 2, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(1663340537063 + 60000); + expect(result.current.lastCheckedAt).toEqual(undefined); + }); + + test('starts polling for notifications with a 1 minute interval using the lastCheckedAt from storage', () => { + (useStorage as jest.MockedFunction).mockReturnValue([ + 1664551009292, + mockSetStorageValue, + ]); + const { result } = renderHook(useMlNotifications, { + wrapper: MlNotificationsContextProvider, + }); + + act(() => { + jest.advanceTimersByTime(0); + }); + + expect(mockCountMessages).toHaveBeenCalledTimes(1); + expect(mockCountMessages).toHaveBeenCalledWith({ lastCheckedAt: 1664551009292 }); + expect(result.current.notificationsCounts).toEqual({ info: 1, error: 0, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(1664551009292); + expect(result.current.lastCheckedAt).toEqual(1664551009292); + }); + + test('switches to polling with the lastCheckedAt from storage when available', () => { + (useStorage as jest.MockedFunction).mockReturnValue([ + undefined, + mockSetStorageValue, + ]); + const { result, rerender } = renderHook(useMlNotifications, { + wrapper: MlNotificationsContextProvider, + }); + + act(() => { + jest.advanceTimersByTime(0); + }); + + expect(mockCountMessages).toHaveBeenCalledTimes(1); + expect(mockCountMessages).toHaveBeenCalledWith({ lastCheckedAt: 1663340537063 }); + expect(result.current.notificationsCounts).toEqual({ info: 1, error: 0, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(1663340537063); + expect(result.current.lastCheckedAt).toEqual(undefined); + + act(() => { + (useStorage as jest.MockedFunction).mockReturnValue([ + 1664551009292, + mockSetStorageValue, + ]); + }); + + rerender(); + + expect(mockCountMessages).toHaveBeenCalledTimes(2); + expect(mockCountMessages).toHaveBeenCalledWith({ lastCheckedAt: 1664551009292 }); + expect(result.current.notificationsCounts).toEqual({ info: 1, error: 0, warning: 0 }); + expect(result.current.latestRequestedAt).toEqual(1664551009292); + expect(result.current.lastCheckedAt).toEqual(1664551009292); + }); +}); diff --git a/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.tsx b/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.tsx new file mode 100644 index 000000000000..fef04e936667 --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/ml/ml_notifications_context.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, { FC, useContext, useEffect, useState } from 'react'; +import { combineLatest, of, timer } from 'rxjs'; +import { catchError, switchMap, map, tap } from 'rxjs/operators'; +import moment from 'moment'; +import { useMlKibana } from '../kibana'; +import { useStorage } from '../storage'; +import { ML_NOTIFICATIONS_LAST_CHECKED_AT } from '../../../../common/types/storage'; +import { useAsObservable } from '../../hooks'; +import type { NotificationsCountResponse } from '../../../../common/types/notifications'; + +const NOTIFICATIONS_CHECK_INTERVAL = 60000; + +export const MlNotificationsContext = React.createContext<{ + notificationsCounts: NotificationsCountResponse; + /** Timestamp of the latest notification checked by the user */ + lastCheckedAt: number | null; + /** Holds the value used for the actual request */ + latestRequestedAt: number | null; + setLastCheckedAt: (v: number) => void; +}>({ + notificationsCounts: { info: 0, error: 0, warning: 0 }, + lastCheckedAt: null, + latestRequestedAt: null, + setLastCheckedAt: () => {}, +}); + +export const MlNotificationsContextProvider: FC = ({ children }) => { + const { + services: { + mlServices: { mlApiServices }, + }, + } = useMlKibana(); + + const [lastCheckedAt, setLastCheckedAt] = useStorage(ML_NOTIFICATIONS_LAST_CHECKED_AT); + const lastCheckedAt$ = useAsObservable(lastCheckedAt); + + /** Holds the value used for the actual request */ + const [latestRequestedAt, setLatestRequestedAt] = useState(null); + const [notificationsCounts, setNotificationsCounts] = useState({ + info: 0, + error: 0, + warning: 0, + }); + + useEffect(function startPollingNotifications() { + const subscription = combineLatest([lastCheckedAt$, timer(0, NOTIFICATIONS_CHECK_INTERVAL)]) + .pipe( + // Use the latest check time or 7 days ago by default. + map(([lastChecked]) => lastChecked ?? moment().subtract(7, 'd').valueOf()), + tap((lastCheckedAtQuery) => { + setLatestRequestedAt(lastCheckedAtQuery); + }), + switchMap((lastCheckedAtQuery) => + mlApiServices.notifications.countMessages$({ + lastCheckedAt: lastCheckedAtQuery, + }) + ), + catchError((error) => { + // Fail silently for now + return of({} as NotificationsCountResponse); + }) + ) + .subscribe((response) => { + setNotificationsCounts(response); + }); + + return () => { + subscription.unsubscribe(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + {children} + + ); +}; + +export function useMlNotifications() { + return useContext(MlNotificationsContext); +} diff --git a/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx index c2b00a176c08..65e2a64ee318 100644 --- a/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx +++ b/x-pack/plugins/ml/public/application/contexts/storage/storage_context.tsx @@ -33,10 +33,12 @@ export const MlStorageContextProvider: FC = ({ children }) => { services: { storage }, } = useMlKibana(); - const initialValue = ML_STORAGE_KEYS.reduce((acc, curr) => { - acc[curr as MlStorageKey] = storage.get(curr); - return acc; - }, {} as Exclude); + const initialValue = useMemo(() => { + return ML_STORAGE_KEYS.reduce((acc, curr) => { + acc[curr as MlStorageKey] = storage.get(curr); + return acc; + }, {} as Exclude); + }, [storage]); const [state, setState] = useState(initialValue); @@ -44,21 +46,20 @@ export const MlStorageContextProvider: FC = ({ children }) => { >(key: K, value: T) => { storage.set(key, value); - const update = { - ...state, + setState((prevState) => ({ + ...prevState, [key]: value, - }; - setState(update); + })); }, - [state, storage] + [storage] ); const removeStorageValue = useCallback( (key: MlStorageKey) => { storage.remove(key); - setState(omit(state, key)); + setState((prevState) => omit(prevState, key)); }, - [state, storage] + [storage] ); useEffect(function updateStorageOnExternalChange() { diff --git a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx index 9ea6aa1b70f0..cd68f366b57a 100644 --- a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx +++ b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx @@ -22,9 +22,8 @@ import { import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import useDebounce from 'react-use/lib/useDebounce'; +import { useMlNotifications } from '../../contexts/ml/ml_notifications_context'; import { ML_NOTIFICATIONS_MESSAGE_LEVEL } from '../../../../common/constants/notifications'; -import { ML_NOTIFICATIONS_LAST_CHECKED_AT } from '../../../../common/types/storage'; -import { useStorage } from '../../contexts/storage'; import { SavedObjectsWarning } from '../../components/saved_objects_warning'; import { useTimefilter, useTimeRangeUpdates } from '../../contexts/kibana/use_timefilter'; import { useToastNotificationService } from '../../services/toast_notification_service'; @@ -61,7 +60,8 @@ export const NotificationsList: FC = () => { } = useMlKibana(); const { displayErrorToast } = useToastNotificationService(); - const [lastCheckedAt, setLastCheckedAt] = useStorage(ML_NOTIFICATIONS_LAST_CHECKED_AT); + const { lastCheckedAt, setLastCheckedAt, notificationsCounts, latestRequestedAt } = + useMlNotifications(); const timeFilter = useTimefilter(); const timeRange = useTimeRangeUpdates(); @@ -280,10 +280,29 @@ export const NotificationsList: FC = () => { ]; }, []); + const newNotificationsCount = Object.values(notificationsCounts).reduce((a, b) => a + b); + return ( <> + {newNotificationsCount ? ( + <> + + } + iconType="bell" + /> + + + ) : null} + = ({ pageDeps }) => ( - - + + - - + + ); From 764fe0ae43fe54e515ae8afd105df7028b0640c7 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Fri, 30 Sep 2022 11:00:19 -0600 Subject: [PATCH 168/185] paywall fixes (#142366) --- .../common/components/paywall/index.tsx | 29 +++++++++++++----- .../common/components/paywall/translations.ts | 13 ++------ .../public/common/images/entity_paywall.png | Bin 0 -> 222838 bytes .../overview/pages/entity_analytics.tsx | 2 +- .../public/overview/pages/translations.ts | 3 +- 5 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/images/entity_paywall.png diff --git a/x-pack/plugins/security_solution/public/common/components/paywall/index.tsx b/x-pack/plugins/security_solution/public/common/components/paywall/index.tsx index 53c21172f031..ee93861db2d7 100644 --- a/x-pack/plugins/security_solution/public/common/components/paywall/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/paywall/index.tsx @@ -14,13 +14,15 @@ import { EuiText, EuiButton, EuiTextColor, + EuiImage, } from '@elastic/eui'; import styled from 'styled-components'; import { useNavigation } from '../../lib/kibana'; import * as i18n from './translations'; +import paywallPng from '../../images/entity_paywall.png'; const PaywallDiv = styled.div` - max-width: 85%; + max-width: 75%; margin: 0 auto; .euiCard__betaBadgeWrapper { .euiCard__betaBadge { @@ -31,8 +33,15 @@ const PaywallDiv = styled.div` padding: 0 15%; } `; +const StyledEuiCard = styled(EuiCard)` + span.euiTitle { + max-width: 540px; + display: block; + margin: 0 auto; + } +`; -export const Paywall = memo(({ featureDescription }: { featureDescription?: string }) => { +export const Paywall = memo(({ heading }: { heading?: string }) => { const { getAppUrl, navigateTo } = useNavigation(); const subscriptionUrl = getAppUrl({ appId: 'management', @@ -43,25 +52,24 @@ export const Paywall = memo(({ featureDescription }: { featureDescription?: stri }, [navigateTo, subscriptionUrl]); return ( - } display="subdued" title={

    - {i18n.UPGRADE_CTA} + {heading}

    } description={false} + paddingSize="xl" >

    - - {i18n.UPGRADE_MESSAGE(featureDescription)} - + {i18n.UPGRADE_MESSAGE}

    @@ -73,7 +81,12 @@ export const Paywall = memo(({ featureDescription }: { featureDescription?: stri
    -
    + + + + + +
    ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/paywall/translations.ts b/x-pack/plugins/security_solution/public/common/components/paywall/translations.ts index 0062e7baa411..a78fda1e90fa 100644 --- a/x-pack/plugins/security_solution/public/common/components/paywall/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/paywall/translations.ts @@ -11,17 +11,10 @@ export const PLATINUM = i18n.translate('xpack.securitySolution.paywall.platinum' defaultMessage: 'Platinum', }); -export const UPGRADE_CTA = i18n.translate('xpack.securitySolution.paywall.upgradeButton', { - defaultMessage: 'Available from Platinum', +export const UPGRADE_MESSAGE = i18n.translate('xpack.securitySolution.paywall.upgradeMessage', { + defaultMessage: 'This feature is available with Platinum or higher subscription', }); -export const UPGRADE_MESSAGE = (description?: string) => - i18n.translate('xpack.securitySolution.paywall.upgradeMessage', { - values: { description: description ? description : 'this feature' }, - defaultMessage: - 'To turn use {description}, you must upgrade your license to Platinum, start a free 30-days trial, or spin up a cloud deployment on AWS, GCP, or Azure.', - }); - -export const UPGRADE_BUTTON = i18n.translate('xpack.securitySolution.paywall.upgradeCta', { +export const UPGRADE_BUTTON = i18n.translate('xpack.securitySolution.paywall.upgradeButton', { defaultMessage: 'Upgrade to Platinum', }); diff --git a/x-pack/plugins/security_solution/public/common/images/entity_paywall.png b/x-pack/plugins/security_solution/public/common/images/entity_paywall.png new file mode 100644 index 0000000000000000000000000000000000000000..9c88f4670db42d9abe42f46e76eb74c16b5a629b GIT binary patch literal 222838 zcmc$_g;!f!v^^Z$ofdamTAbpVVx_djio2B}CAdp)sUR)I9f}pFIKi#B7Z27FJb?fK z0_3Cb_wK##KlsKtIXO8a8QFWUwdR^@?seX4YpRhEGZF&;05bLGDz5#kii5f~?Y!&MdBKq?vLZ2oqRQp(V&61O=RwrcwH1#}URHk= zn}%4Taxji3#eu)(#I!~r zq!Q=3pazx+;d=h%cn$Qbi~Mpn{IE)Sq#4vH3O%e}-j;wBttvFg4^*DtJnKlUT$}Pi zH?1lJCU}A@;#{r&dhZQ3$-j*)z)vGjpSb z3dy`#acT8k!(GJ)jiA{GH+>3=rk1KipguVgK(MI2SRe5#eqC6mV#YwK0EnlnJOguHFoYoKq4zwh5M@^js53KH}oz`jrPr}y<-OG z+STJmmqJGt(7AQ_+3s;B_^D}p$?E01n{MYD!TQ^@hMX9UW!Fmu(3u++=saYm)u+Zh z6f~|r5~0%e?l}s$iupU>PNG&!W@-sK_b4O}y(W9((~4=5)51OeUZ{by<_ALBF3iR@ zNxWXZma0cjoj@xuzbS6#aX1y$wX!U+xuh|$tv_5A)?kw8fEl6jjC>`f+o z%V5pZLA0Z`qjKBz5-iRVl4-XDHLM(5+?H`!Dv>~sH8ukw=*6SJ-ld?m-P;m$NI9JG z?>w4hDgI7ot^V%X)g<}|BY{5SZN?C7HhybRTs$qTQ!Olp4h{}V`?*-%v@}pHREdBb zKbbI*{VLvg2RfBF^xaQ@-`|fCXk4y$&53@aivtJcEwQ}ZmwwmO0RsQbACr!jK+pTM zr1&?=GcU0p#gfz6h*E4jyBqMe{G_bbUt0(auAE;_{`H?W&u6F*8fPA% zJHt%8%RK8LpRim;=dE7t-n!nh?qs`OYSQ$Lm7KktFaGRs^V&rmJmS6gXGu&hAo!3m z-iJ(mroIv^+-L+(iDPzNEp?tJ1ev(DxtlsSxhp_M8qtcbOy9djak zXZ{l~1G4WW?x*APM*(5JvdiX4l><#J_)b96U-PZ;8uILi<08&3X^Rp<3!BVh?Y-U| zou=7*wP!fi%fXX_LK3L+`t~E!puwdM*9Q19i(wnGb@kL|`8X(g|De}rf5D~guKyz?uHDyW>uzf*`@8BDyq{y7>ZO!VSb_SPTrkd%bKoD zEED$4;|k7A@rRAeM<(rAa16duY~#7<2#U@mp30JBfwS*%z2SDy|RwZPIK0u4*>-Bm;5@<}Q#7M&tg|qMB>Gk3H znEXWLi1G+H!alfW?$*_vyen|EPY~=?jSR+G*hb^XqM_pBVNk7B%yLM~&YjQZ9$_jI zKK})dCfSm|UW*}a!*t6F!jBt7&)a*Uwg88Yki0)e8O2TMO^&jCzfTtdj2 zd~>@!uQg!aA8+s%{BitN@!QVS#@ebtRn{|S;rqGv4xVF8^7G4b6``AmK~1AuH@C{N;;pLIK~N4uu0e2_WN**$JnBrqY6XFmN1 zQyh#xaV$B`_ctOf;W-ZGKAE0aru;Ae`nSfK9lZ7L^ercEI_J)p#xa~Ln8PY{zRI=a zUyhY)S__*pjTSiUF3Kk0AV$j%0*nNR@$wkcuM-A3Sb4#P_%$s$i@S0A?Hg6|p`eVT zRi1?)(LhvFwyf#$v)=(gR$1wRU%{>|tj?Li&{M8e#_~J+xq8R>w&n_a7wg)7W>jcs2mjV0b>GzNrv6!?^A?21_Gz@|9srcwjYTu^Uk98tjdz}(1O^3{B&-fo>2jir zF69E5=29Fp&nKiQY(5T@--v@ixS1jAs{7tcn{yrRYu8-(8iVz7L1RF6o0-O?7EL8j zr4T?XM)Ex72$Ql^f@a&fMX}BpAgAsy?9MILKslJ?08CS-mT8s# z$W7-Z>$}yaN;pOXMlQGSN*V~Hsis_7VU9blv~1pQZ~GX)6tfRa1a-UtrQ8$fJq+nv zkzd=~p6cn{W{dj8G||R3E;dE)vM4!;E`+^nTDzjpbnP3^SX&U9Xs(QRozal%P^G>% z;oc-h&NS~}Yg%yF;&M~Fys6f5SR2UtNGu0es74i1aED$WimhF7mac^B{mpFU>MxHY z<2!qz{GB>{ zGzPK9_Gv27Jxd{$lIQI=H$oCDw#eha)YUzO{|WKK&iT8vgdTTR&>EYM{~PVQw*;-2 z#R2ft4Q99#Bl#YuX0CM!WM2+ypkg*jUHz@IPWABT@_KTp;hZBC4r8wX-H%<8sqOYd z$RRFn^LQ#vh6|J|ir&QKRp?k;v^_(phUNCTq~-n-rn|(nhLynt85n~?R=3$EQlgjJ z@iS2&$TkGC=u@OWUB%d~(n!M8eWVLCA%$(P7r zN9{Y$)ur3#V!?M=c469cYN2UtOEN^uT6b$cphNX883#))3#>_DCbt8j=WVs_6A~RX zrdcxs%_)bC?|!$>huAh>s?}azxj?ctZ;`YIg(Y?D4F*Iez0pl^D{;?5z~B6 zASu)Q5SnF4iY_x@zFcMBAF@UR_k%TO`NApkP)p*LndkEwmgU><5>(qRa~rDFze5xR z4$7QgyQ4*@XCo*q2&~}@{k_`N6X&SCWMDaqZ^}j187V*4+Ods(gG4pf3axkw{=XdZ zzf1SOYq)lHv&Ns12bvq0oEIXW{VI8FpYj<7xpIpDvr;DW5(Ig^mg z7@M#D6W|UHfUJeZMF$~k{h3bU;QIp!yg5I%*&YWUok{C2yC(#0#h6Z%q|VL1bTtft zO8FM-n)y{{cNAZt=dQM3?c31(b#v4tlD>#GoDr3TK2E_n@ zDdlptAO1pXx{d^bP439SE%28IhOO{F&-~X#L$;VDS21I2G;*#r9PITLbmX!KJqKHi zKS()r-;1;NZFRIEUiA*n6kWK;LlcESm`?6ln{O7u^>p`p8XkxXfmFogKFt!vAj4Z8 ztsLhHD-RE9CU&O5jey^DDRJ@f#Aa*iK3_CgGxd-;3c)5n-@p2lL(Q}CZ3%k3Rnqb! zWhQjr$^)E;Oc?pY!=v^)V7wePQqoEtnHx0xiTDmyfyeo;#sQ5^Xaz}**&1{mN$P^o!n;N&F~bdZY*+I@GhLQyi? zvb=XYk0M>gkpIo`@B;$BPUXK>|5IaVp)h=?TVUMU-2MvYM5)GB1J&tSEe#^5qFI5T ze-m40@=2Q{1Aqs%j+eqbWMM9gjxbI&PZ&9kRRF|rR@!|tGGowvMh29Z`94SzgA7ps$sPC6 z|2rPa$hUB)B$NF_&OJ8F#b9kUgEHvr*zP@|6(fS}%Yz10@4@qEn{_|VSVJX;9=>9= zRWc6kh;g+;DfXv#xAY8`xyPI1Fwp@xHLEfOM9-;qb8R_0fjORS`}5-z<2bkkGG zug1|i#uE0FnGUb`c9DEwkQ1TVQKpdR#tSD78I(QxI%pvp`k!hd_w60Savk>ePH9p( z0QBM@?-}~Fp)tb1WYUj!m&hS0w=0S8bN7f;NVkBmH{$K56(x1Y-)s)X za0=W#sq-YUn1JF#o#!P$uG`;QIfUlsZOqp{b8oLW|BsDEn%dEpXQ!%kFM@-E8zKlO z!h@=1?Kt!a>MPrc&1&;D=8jl?)f?;~1K9nVT2xg#O?w2+9!(Mv%U|L{QaZuna(EEG zE#U8dM;UUK4{n=%&2BXG1<$QMC1`bB9NQw0Kn&0IernZ+lat}9d(5?CJpZago5@<1 zu9H8xqe*Fra{RX$(DUF!(m~2d-4e}~hkNRvzVDY)+g|-RM^vP=l?9=LDQ5xWIDRJ# zD^3>V(!`9%xJrZ@L_!X<+0LWQQ9a>DY|WqeC<*PQA#Pg-vyb(U0l?+K_~1m~6kzj9#bR^Du<6c!lng-x(A zq`IzeB9SeYrQhb~@L-@_idP8B3I7qb)KWK5T3VWwt*vDu-N=xpw1}ydJzE>@FNFvc z>B9(wKzF`DQ&%1CLZikl-oG9wM5E^`53};XFCH?_^oU_;FQP`Yc|6-lhu~#tMcVlB^&Bm!^trA4vKDrF&j%p@O3sw~qU&fe$9 zlb(XUMa87zHO;z0b+x8ttBx0$V%Av_#&I8E@2Cos<9bqM$#@SBeDpq8AY@{Z7aHJE zTMz-*KKxA8DIzQce&g|FEty^%O{2Jmq#MuVr}#V(w5=uBEm&qM!C!g*>ytSuk{8~? zfCybe?0;~uZ-nSzy>cE@`iuYP_RGoa*zh%bGfBsF*$y-gT6xN-cD#JfVXh)re41m_ zD0Qb)&UUc4!}3#HaCc6cUu?;1HfopVF!J|?HeI#0gTjV6tKZ2&X`W0h`O)v}5+Vaw zR){U8-$a|xBe-2CLDaHhQLGM8cFJ;3^F#Bcy0mE%h2-$#)gNRf;@U5^w7{PXsOdVt zeaK~_i`@L(hbt#_RkJ8mf~RCh)fmqI6#iDy@GMvKT*~)q{Rj&sbK$OiP%&Q zeAw{YkE+;{RAk*A4=KbSGn*wiNUdkKKXFy%vQBy7O>Y~4-pG}^q1yjkhV}VHk)7HZ zA$px)W?DwRSf+W(^C@g4Nobi&IW(oi{{i6n=ck9E=L!hFVasaH^_K%LAJW!GGCD+k zb5~l@6L!!&1lY#S(=*3}ca(n;79@f9U02cMSV0*4( z#kviHS)FaFmZoJH4!3FP)U;YeKJO!_bqGD7r~b$DAxg2&YuTyEyPO*R%-JyU@$B3* zJ+XRx(|e}jQ?b^sz$jm|rhqzAz^Po`n7_%bybtO6#qP{-w0`#DiTCw|qgG74ZReg1 zxRs505ac&1tA5$7OEkGW1U11z6s;F&zTG+Fbcn@a#llLaciWO$*#1ty3sLDPd|I<1 zADSgcw1>Q-RZ<)k$Yqsrei*&h|uod$s6(jk%A)GKlE_1U8t4V0G8dht$ z99PPTky2G!)ax?*jYQWG|J-1gU*xfm$_IG)ncpJNotTej z=p!`G=0yT3Gs%8@);;I+l2#T&*;Q66GN&GAfxkH`LK+iu2a95(s`aiqDh}i&z9$TR zJx9qzpK{Hq6a_-c+SI1YU8qsS!>n<6;6&~eEfy9 zgEV|ULV1#l-K~-$DYsY5KyXGv9G2`I(dc3haAeO4e(tI@U~WD@GTDfmW|R;LrekjwVVLe#aI$cQ`hKS2g8^Jl4F~p%Lx! z&fv<2+trokz>iS`qiI8{eNp53xE_4}nF6o7$lB48=cv=7`gkPO{`-d8EAWdA`p4Ko zU1kX_=WnVv5A+{l;z}C!@X7;B3k=iWeiNQiAejuZ&3Fv$5rVmEbdgEtek5 zXVk2*tkKU?7lf=5lSxLT%Bh9TBhX!NTuU6(Gf>Zya^r(?kEw|Fi2W`EV@H7|6}pv_d@@G07i0VsV+-o z#{2@CLZE8AEQxYiB)xoyMOF$YRshv2P0dz|lM*kOmo9mSL7PzwPc!oAh@6l=VUI?2 zK;93rJ^lo!du6Y{%e}=0qb}hvj`0sXROT|LLqH$rFL6Z^KVn3Q(62VFbB3~NlP)3j ztDU*M>+C@XWhIf2G#^f?EiLmF-g!zK3y~;9)X*%?KO=9Ba~np0fC-MK{H}AxPpBl1 z7G_+(%>aB?y;DVGWy_0slGNSL4~(TaRk7bZ{Ji0iTku?1a7`=Mr>TfPxOj^&>+>T( zGo10@>7Zd|RxtD$kL>ftTTfAWV#q!6S%1f&J;HS&Wd}`{ovN;+F)C{&$Ga0bUis2H(*w{ z7Ii0y|DY;mA0G}E{qJZU8c$Du`q;|FmoG;Uixb-iM*IUs&#iiIZw?M@6O7g3nZxo& zLl1uDH1UW6lBNd~@U4o9vSnms-X_Gy%iOzK|28}z)DJincIf*hn|#Bl3YZJbCE-JWk{)M!zkZ%M;CrHOrmz{)?)Vk*Tkx-@ybv z9_}BAf9U_R(784d(G4cQxzT<^yHI9{@xJ$3SX`W)ngYcq>-F|ww;Gmg)y%7<{ItO? zdIp9?;c%@iZH6nRfy&`A$)hDm&y01)qDyp~=6NwOkxcd4$FVV5Vy|D%w%poPt2%Dw%-4MOF$65aRg^HXA+M}(y$Utef^cu%w*9PBDifR7)k(DLjWW1|L23bU}}TT2oF#88{G6)&-7P&HQ!yC&hZ3oJR?@5Tn{B4UB;| z`zVI8zQoT5a^FZ7&_`_v@|VAp3@q2s#+GZuf%~RwPB!$|J|?#I57uh?osLfAe&V@XZ)?Lbz( zvQt|cmCGu4as}%;-90Ys_GFe<4>#kgn zQ73O7x}K^6=^j4yXYfN!h&Q%NlUS^5#biEz*Bs`P>~J}H7ak=qB0aAkG2hYf7yVwJ z;&Z5zlDMEWy~ll9BtM?*eG~x#C*Hn&t5WvW7B((#f9F--e*QVJ(aP<{>0oAK*%9Hh zi}1yBaxM~f%`A4;)#UxpG{gPyrZ_VJp_J5*>vxsM5!`mCNFi{yPAcR!{Nl8P=itQy z$5)!zkxCrNX!1kdpuu=&$4m)1VgyV3pZ@pm9IDt-f|i5vEI82?cs!b=zV_v>7P~9a zn+^HmIdY*rTVt34chRVD1Mk8O>%1gUF}9kji_j3w1u3FcK15{vr>wU-=Akfk2bbS2#CHm@0?^gDeLo7V49Iz&GZ54D4x=)O=HC@b-#E2U( zZp#|6(R6M4nO}C z51y1?ycxKYN&378e(LcSodZ)xYV-3s&KGPGi1>9mO3Dve8NlQh>z#%6zFiGz^(D3(lmkgZm_ zN^uf>t53Z}`0A$`oZrR7c$G&~P~4cxMaJU`uW|eeztDo7THEiRBAL;6;%i-9kYvpg zyaE3_rHCBd9Vmz8j<;2z(fTUuHct(;``doo(eG*tKP=NL;k&WNHtH@>Og*%A#Pa`= zKK6{#7C2UCu6d?OT~9vy$4EYYy`RU`!G~F^bKaq26F4fIECo$XsT=!uI=w?@&s|1U zgZtth8lHageZieWSF$1YNnQ53{@|L`kY$}`RY3av&6L>yoljOEHN8yHy;YSMi;19L zdM3yHTW@(p-Oxf(_d%BR+`;1 zFm87jn=kca#Sd27YNBr!>nv1TIq0PA0D1dmp8?)Q)whg_)WrX=C-&I4PO6tYfD~Hd zb5ayN^5e^bb&A*kfj%fA8hx?nqm z8ob73V`k7X5Du8g)t{CeyaNi$4B&p+OK6dpJ1K!s{>gnqLe*g z_C3v3oWJnF(-?X`KGwYDXS~Tu4bS>Rtdb+NA4U3;%$`D_%;-Hs(alvjYx2zA>)qXO z`|X)?UC)=h_`&npxro8%t8YE%a-URar9(9 zec;t@Y!~-c(d+m5&ueOG*lW0$fPscZd~EDH>Y>sXX-qzD9?Y^ov&lW*1O=I`m#Y#Y zq7kmRyv-Zek`b8#wCq=TSu&ruwnmx2p{|+-{ms{(s%W<~>~Z_C%Q}ShOlvm7s+4yl zxdCz~KeB3+$rgaYlec!c~E0}y42}mp99(1vAfbl&%3=hM)s@52bRUOLV#>_ zEf>?Ee=h|kEy;knJAmZUdax;JmQpp+y|%@{ipGMewJdyn?1W!<AZ*uP5 zoBYIFA-gA(#Ppnu7Y4rt%M!yj zYE{B}cW2IzXIuu`52S!)y7xj)9(9eMHnLuVUT{9~z5cjF1fG#2c2hGo=UO@5Bg6Q; zuOoXi9;(C?_(sgYH~ zt_y(7Z_x0ab>(1rIVgyx zIbSVDy248k`hdqP8Rrw8|9l(vQ7!54$0t8MTfS#&mgF}{_7~=3cV>(!T}6Fp_5o=k zv`bF7YoauUNQwR_vSS~@CcqyYjamXun zV?!~%<;1N83A688Uc2>)zC8W5`u+PUhpnySHY8y=E%&dU6T~bGSgVa%bqjiRI8lH3 z33?%5jgA{8V6`y*L)EY!5ol$yt8IaA!Z9^O%A$p>#@+RWdYmy{ybotoM_5?+ZaUiR zT}jz5ikjmwKVis&q}=O@*k?&L21C-G!cU$!{BjcUIolI=M^wrBudG(;^_QY3+@o`NfV`Bs47C#HgOu^RV+L z4!;3?j*?sldHxhWSOFyUN1yi3*N?c#C5r>)Nlqb$3T-yn4`uu0@CTD)-4E!Wavlq_ z(bL~gN^<=C{LSy7ThFi|j>+WSp-0lhb51$d1L}~cWqMh75%=NnrpxJRh=_Tmuo1!Y zOJqlR`V$yOCxdVShs*NkiIB}+m{kman zFpgU$4yNtdV;xK`kJ9rcC3?n`yE2qX8Hb3Ye8GL*$%uzSv_?E0E6JLOH_SkVp22}< ziwpFn%-Bgu_P>X6?aOAl-#oFRprAk|C*S)ugABdPWglmRvM=H_=oYRs5Y2kxlW|MlhjPU3{e>zY5V#oWkR zhOsEI{yuAhx3<(jA5q|Auuc55#jF7tMCCMA6Mh z?T{L~{hDmYo!#pJ&I9^biMD7co+$`N>(|}(L6<2jl`8fr_w$l&RwkwmK0XrXfUANG z1KtM=GJ)S-qv||1r6nXOgzH^HOD2SdK5_TCr#EVboZ40u_1+tR9nfCy*p8G1C z+hcas7dWa}6R=qrl>V8ndtfb1LOANWmn~RVA=CKqkg3$Pz@o|?>BG)Y_c1wOl}EOK z>vP(wJHiD$Y_@dt3AT7iVoddtyma#NJy5|e;gggI9=%E8bM9|gKDfoj+p4_ckEb0= zu$6d|smJ9CZQi_zl)fa+n#5^mt7<>=I}bdch&!f}0fzBNhe@vFfi8M97vj_u3q7!$ zxgR;}J((mTb+=@(#u;JXyK#-wF3n!iQQ1t5{p*?Lb@)~au5=Af;JQCsWqUf{K}w|mQxWQ5}9 z>P5s8+d!Jz{wIk45w#b(mK$Y;8*j&2Z?9#IdG5;3sd(21;0$&b34?y>XQdJI9wV=5 zcSM5Bi9~X98}=F~g(~bS$GbJbV4Gr?Bl1`vprWD=_C4rnS_Gj)g4m^yHo#!ySqFtOq-jdW`4s=ZkXVi?sXz9(Ov^@X6}R{%IZr$>3Vip z>8_}TK~n`?`=`DpV)5iVWl%`G!Y<7m*6qzBAl|TjcVj+61$*<)Cn{khtw&uP3q;6$ zl7iP|AFBFTAVx~A;ldH(q4F_g9Vr5_dG0`hIqySS_3Lw2pi^G^2miCI29~gm4Qsn) zo1bBg?nP2vkaOW_!Gzp#(a@f!T%H_t(uRt*M$Q1q9+yb$sRcz?gk*8vSjQeKgS`>Y z6K8?rb@%GYMV52}-GPCDud?_#qxtz^(N=dVC0GZtZ|-W}-mb1sEs{MRD-?W|m6i$@ zrl_e)x!0nkW7grJPkV!Khh*JMM`gom@E_@6K5qoi+lW!%Fs7f z{90h4-$v)_h~3WaE=`mY)}A;}c44{jQ=Q3JT{%e#i&&VJ<<*B1JvCSCpxlg%hAE5w z-{>6;VX?2)cYO`Xy|$s8SWC^*zZ!UWcvQ=I!WvV(!9ao1miDvORH&kZ*7NRq&b+lW zf@+pdU7vlONo^jtm@|@?59sVqQGrEDrA=QLHgS)fpbw7w+*qnjr%#xap&i z3MCHZh9cfe8 z7YsSx6uY?55fKyXyHj{(WxFCz_!DzmG;J0(t=H-)rlVa}_=Z1tl7WL!gpvbm4I|z~ zibuo*<8n-oaeVr#4zyf3dsB{UM4*cVJfAt<$$4A*JaZ#Aa854lsssbBzU>YZr2W*M zh{z}SOo-_3zo*0dN6)eE_V#O5hv5xe(6?s2gLaS^uV`9^B8qrrXT|o^%Qr3AKg zt{j2YdW2$=n>%p|5bbDm@!M9rBLFYbIX2FL)TY{BfHY`LP0U>&Y`SN-)Je$waf z+1m;sK!%*9)|@x+5)_JOo&^}=~_uX%$uX>!AUSYjUjwwvIGVSgDC zlnTJyAhspjsp28G4SS-B5ZVLo^o^!Y6pri3;@p`Rhw0w_nytQPQ3jB*u23iRz--FI zly6>54HavW3QGN`bMIEc+isE3E0rS3X4pcb2p>eMXk_%9c(g@Q{0!#uJots&ovM62 znl5T;0DPJcfI#o|BOgYXrD{6cmiLmKQ?Hc@wT)MlDiLS&65;O1k+cRB_$mre*T3~V z-OS0WcETK3XdmeDSxc6`0>3PuJy!=dE1#X-LYF3-Px7}$LC zK1xZNT%>bXa!al~NS#|o>GnD25zOxXaO%isQMNz^Lc9~X`^7>%MZgP6{@^_tE5q+j zUNdyv!tCy+V>KAdTY7Hj#Y6bl0jF)umC(jC97F!q!mry~nQAZhLsNfeH{cWkYqQ2s z+zP9ZGdso8a1e$!Yn1W}cHM0*NPNSbyXLhvZgS6e%gMe%cZG?a-_*ck6ac2P9+-x&9_D59g=QIq)!9C}zSgQ}z`|P?56f!OLC!i(cTB?!^kM6OcYv z;Y^YZR>6lHXYHXf5$$V8a|8|$aWk2;hacX&c%bsg4+Xqh&)mB0y1HI^Ax#ahh~g9} z*BhA|^HBZ0U4AAXh=2HQKILHcIYmWGE?wAxxy|LSH&$S^5<80y1^&yIcxO=|=w#({tKg~7~kb0VVyRGoV-&bO(ZX=$1lS@Bx; z6lXjFdaIhSq?Ee1VH}6%vwIqO?f7D)1*2nQ3~8wVpzM3r0V_X0&9T%WQsGxNr7&N; z4BWp$NI@sC`y4uN-8al(@IF%SvhQjdTlD-a&DYyEO;m_qu@ooBYF^`!(j?nQub@34 zFnXj$*(F6gRNgcRh}Ny~*mnIx(58b57atNj5rt89>;srD*-E+(oNr&-=iWY)yAcYZ z|32dA3vP3#g6vx>0Kau?nG0XT2f9<)=ThzvE1}F%n#dD_)aq&!SU9q$X|Fwpz}b4u zF%uJ@jFjN3$BWyylzIl!>t`js*4M|ICg093^I+NV@CeK;on2-o;4kdOVXjTt!gWh9 z#QY;)4hwC$t-gwXz};~si}I#BGK|tj9>9VcH{RyI*ng48r@>t z!P}@wd@@JfW}Y|d#CMNbuK(t7u$c6B)o@r~j0oE!%HgLas#gosGxp;Z->lk*y(&l2 zs;l}0KwSFnTP}r{&nS>PzuFhl2jku6wBKoVc+_1@YN?tOn`R_Fs$DPrqCAatQ z(u<3v!R-;U20>S%_+00HNW-WR>Csgakb(kyT#i_$fvs2N-rx~ZQ1A%}^5w>i)CF5y zZmP`4WJ#b5zdi?4Nh%HQuK@}>Z!Oo!LN4i}R0`wMtlxe(xrudN)GQl%_khzb6K9yk?7mJYPl3L;ySaeX zoivr4(A7t=dm3Zr8txzML>kSKXB1jQC33osum)OMSkU2k>Z-aNl;i zfQot(mpqlM1sh0s? z4=Ml#*H9Gtg5fHh6Ui&d>Ffn_=zn4uMuh4HnR%f zi{N2a@zD>$V^V`}q7AFZ-}4z(zkfm6AF66JUO`e3XOw!rd$-9EoKL|j2Y7VDWx#*JQSBv@-{baU(t{s(Tr^Cr^*^~vF~Uu)2EPQ!U}zM_r*;B zG?DO>zDuX|B#bGe=J3kf48P+}8MKwGd4v01fQiR_x+m!qr(#2fVG&hZ8;!HSG*^{b z%lBWphmYz^ib^&6=9JXGz3VwgOQGgLvMV&v;~nw}o%;3=7a~EPE&(OWvN@g(!ntI- z2UzPkj;Q4h7MX+&q*pxM0!!M-FOW6?yxYd7tbtc)Iw0-Zd`m*t6M7cdPL#P=B3ru z%K9IE^!OCm;K&Cj1|c}CiiukF2Pmt5o37#K-$abtOWRsI5r3G_lHE5ljk=Z`SCY^5 z^ftt~!gRY69`#DhQ+X;@6{44+#Y<*`bL6TUG+YC?(xSoFZ@c~kx%#g^I#_En6o0?Ks{j-w`SBz5uT?kPeiGu@LP+hN z!$^Gce$4Dbs_YK~+Ic}!SikVpFU>o-vGWs4Q%Lw3kmW*tPZ+k+yx8Q|YfrG^J(Vb; zoq6h^M76$2^e#_+u5e>O9|7y5TP{NK0W>=~m%k!WRZ?!f6ijU=k0zJ8qUo^w-pRId zcB^j_GRe^9Z^Y&O+KOg%Ub4=HV5O&}0UlH58HVMrie#a5aAIoZHX|rl^^t+9;VMN^LV&vETzQnH`QMsrOeJe zQm8oZfCbvQ8WR1Q0PCOI+%P~8vKR(I3>5xURige8eIGoSrH`VM{F9qXB@SB<$&U$~ z1ztX^v}370OQgw!LDr1Zv%*R5)37*D>WFq$0Qrt$@?EgvTesa@Xbvk{&ndb!stfiv zrirg?JSD27cL|;S^&`=6h$NdF@PiAVCB{2*zp=!_n2S2BFO${bCsvBHCz4JG@Rtf> zOC%{3@$01%=2smqw z!-u0%LRK+e0iX4`9^gAV#-zM(c32C&3P%}?yk;gOo*{iq;ddIXR(+A5Pnu+dHSs4} zzW^NMk34G#SZZ1tg)-xf6aL!Mn2h96?IL?1x{|CGc24RhxxH#R31_XB>uTk0#{*(1DR7?2~Sh zd5wiDjshg#Ka%Wav)?@d4!|?e?VvB7dChA4PP-$>G0DsXB3CZgKDln#qtt-kAZPIU z{`av;K>>;HOt?j%AJr+us{*Q`d+WyLfG{PAhK7FsnzFG6gqsqI=L1TQn7vd1_>0c> ztIV_4#MY9_J!>+CWYo$2R5UoT5cex(ihrTwKc40cxv_(J#xU%~G7PEotj~Sl`RK(o zv7N$C>OP2w2+DuN{@`%Z=#zEmM!`X!#aGVmAa;SHHuzC=W%dM2q{8Ct39~fz@OXdf zx!`2Wl?lyFpfUyHd(z{G`jH~}F(&uh!**0>k(8QeD$@1)Sg8+XR>Z=i5df=u#N%2( zJ2B)B3!?o@#lZ|kl3d7s|Kt1gbhS$=|7Rt|+lTr2Sn>K4HG+0B**Z*qy-Zg{Cy!7> zFZDgU<+ml>r2o0=b1IHWPXRafDRp!_%+xA@XKu*j&6hLe4I?lqYU$C(n9^CG2F z@L;Tp^*Krbcf?+y%*-%O+h|zVj^?(S4Lc{1rrU|9sb~Fx<#{LD0cR)MDFV2M9|U6 z$&>3NIEI6NNKzG={y$oR{@0*AH(-n5WKf$~9UtGQV9oc^dC(M3xO46623}8+ zM8+e#j!?0xVZi*fr-7@a&zp(f7vz10odKGoMwd z{uB}Soq&sr>)@_aq!kdFp|!lUg&oYHpRPEnnrd}VbsyV@1O{4Ge__ykC3o~t z!E09i|D)f&L!M zus`sz;a-SZ@C7ASkXPzMIc-Pt0Ozg0*ijrE26G^H0OOu!*J$lw?+&XpPDRQ>)AhI1 ztut5_+f+MHng*SS8jN)8D1C*NYG+|O?12wq0e+}r@2%3cle_^9k z{AgSnLf;{oeMOA*D2cQpNj|T!hntTb2--hehX)0mA{+-?ha`Ic>(N2}2$(M&P8*^< zF5x>-6QYmkDI8NmN9T_a^l*q{p8CmWefqy-#24wx)Og z*l|hU8mhIoYji)87aGX16=F zpfTz{QWmv>Dd9^8-hq*RK6enYfa~XJoP>(01sM)Dwq8rIJ839%NGwce%NV)=>$xY~ ztsm94A08`+;k#R%lZ*0KxW?Tr1I7h8k ztZb5z`AM*NLNKfI;_0cX`xH0=ad+-u#&7M;7%t~jjXPeLx@((arm_;h*a z2CoojG2t%eYQ)s>Xwuh1hn;Ih<-?%82u^PI zs_sz|gMwg#P{*dH-^iI~X1m%b7iymm;1LL?KMM7ufBgqvoh`R$DLJVRuZ(BsOzehn zG8(=lwtnSA?OETLGHh4jOjWE)^!O|3EGSh;*x+_W*b#6UXxNqVlO?->|J?7)rGiqx zi@MfCVvp=J;nRpr z7dWrC?ZwECOY1MM)HkmtFSMN}1uf!+4yYyA6&x;MO@}nZT-TG))WZws_TuR2z-ZLs%rQ zSX%Va$s?8{A!@|1&r2LbwU=u&JE}%VzyBVrgvQ+-ZY78@uiaEXvytj!gTW;y3KrrV z{PbL^LATlI`a5xUHfP=gSu%`M#u4D0{-orIWExEX$yljNfG_7nUSZXt+U}v-#SzAjS zuj>A7XrOn3ItCj0F{ktMOM(*K%ab=BFMLP-%qZ>wEdB@M4)U)T!QlDZG ze>pnhTaomQ;y~W$FVWPXE9k(qy1Co;YHzK;K{)D@ zIpf?h4Lj?aQL3lJjLb|NDXAed#%DUVVzo|7BZv|P`uX)hC}iN7^u#IP;kvXOKhhY~ z-k?uFFnwkGBBqSk$A<{|rUqJ3FMvpU@}K(O+7is6&EhI?)yU?1bT?sszh!_pM=X7T zJdZOloJ00zpDd>+Vbu*}K8OuJJP>Z9bR&boIHDrii8Zhpd3m44sw&N-xfVt`wy##M zwKyaL0#Ln+WzT)*>V*ec5)k~1bp3zA|EO)v8;kLj&zo&E6cxZcRz+##l1O7gdHZ%X zgM(DMbGv`P56&tF5zjKyJ|V4#6=k!a!{}s$aaN;@z&Y{$kyCt+Y;C%c{rKkd>rcVy zMU>X#x#+dGsK6kn1uur`m8sJIz)3@maPe{K#bN)Yxuxw>&TX@+wI=DkwrCnFXQj9U z6~ZHE_8mSht&~(P$Fd$i&fz_Ir6|Yp${Pomnu~;w7V|w98Y*_x5I%n7x{e$^EtnAm z1c?>Or;`#fG{gJR#c<2ZyPh**k@MXm#Vez^kb?t~X$BRR)Zp$l1L{a!#WKCJbotcJ zTBZG^Bt|vW7L_mJ5$7PO<|Oupq&FXPCrmO2NLc5Rf?=-jXQ>G|l7B>B0 zC_Oh|YSeRU-)4Tuz(r$V^x{au<`NlK%Zw<_@u6g;L_Tp6JN#x{O|goN_H*a4m0T>| z3+`95JAN<&jks+pyixkGz%?W+imw_YHzCf?1|!63L^0o#F<+kqIWWS=);3gG5d#@r z3ZjCEE-g#PV`97+W3wpXwF`I~JRc5}QO4N>xu!V{iLq!IM={-I&}El9`l1Kx1@Lb( z!&5?G`lc6gcrlpmG-DRqx$fBmG>`nD!YP(PquSI|4BSf1bUrOyr>SLTVQx(NUFbj& zTD;rKDM>wtmedhGcbPHRaphJ6qKpzKK)H0g33!f@i!J4h0|pGKV)s%^6O+OIB5|u= zxTqor07r>jjyn8}%jWmQtqrZ{3A|5uK^%YSJ(FZ_KyoiGM_9gWsyOd+cxBWO)kMUQ zrD=Jk>c+~LLLq^a^C)GJxi?&LwLa_xuq>~z#_`Rk*`4;8Qic6JMuV}3XkDNBzIjM~y|Aop&ecf8IJ^-W=Va@Pc{br8ze<@K4d!T9xpW+CS}!Y~!QTDuh{vP0mCEZP4_yh;|MA{*6?g*;mww8TNv zZ;@p5Mn(#NI1`;TqhYYd?kD?4N29;3zf<%-T@h3*Q`c@I*%K-$ETaxwrt&POv$Hu|Zn7pj+Z$9d&v1yjYQaTbv~ z7cq>3s3`$TX)`wd<1vosL@yzu5df;-BpeiF z)N{X&)!tX8Qfvh@4JTh^L^PST?>kV2#XKl&mo>4fDDJ4j=3ft9tv`dB+ooPtdV*4? z0ti#Esk2u1dyNGB;f3h}XCK@ zI)C%}7nbmH2Q5`?q&js>zM^f(4;10qU2CGF!_d>b;=Ot#M3d8eEKW`LNM`F}$IhCT z{&v9)El6D4Xzy05`;vexEU3F%G);ja?DBhhp5<<~*|QXun5|DC*7o-+MK9E)#px}w zx1|UB#?*T7pL}RMMP414xPRNvBA$gef1b=?SV@Gp&u6$bbxlxS)ek zxh|v(4T<_|;SrL>^5x#lk*_T-j{N!a=eyPPh+0`ktav_?ZHLYG83IkFN)l%VuEOTJ z=FhtyaromxH(Sb^RG2mr0W!P!Del+E6?@A`1&-tMIQ zb%w*ib?|+=a`mMLrPp6!qt%JjWl99m@U=GpT~oA%$IIn@dogAh*vQPQwE&y#1TaQe=!6gHud%XUw z5#OH_u5*p}w4ARO_V8K8eEX-#TMH3X@Yy{_>ru%|)f?QE#OC+d2p6MB@>G;hEw7Z4 zkGEyFYOf~TcfAC0&%@`dj(1Up;ggT+oz`Bdw(`ww5&joiG>xxHi&7S)b%lCJU;HM@ zf+4jNaZ+a;RH~FlbhyB+@3_o4dvb?fP`y$glMp8^m3{FxN?>%49Z{+bIZ@G%da8;dAxOi)%l+ zw}gWpOVQZ@mL#0!>1OB!$-D%-?yH;}CIbMua^eKd2;vO6hy| zxK196R1a(a=gPbcow%u8%xDoj4PQ~=(&Xt|lU-qCd=p<*MlyDW#bT|NTQRWg5j+W(1xbTSi7U{?K=dV+`SGPw?eX4h~!=&SS!7#Fo!d-4nPZ?|dLd+aJniH_-zs)685V{~BwxG9lOo2jF3n~S;?OfEt6 z#0m-uOvOlDDCKiJ{V^eDKDV*Xd05aT(ll&6UQHIHJCC1&oZGP`zfvfUU=6#~Fy6yW zUySrTbTJx5tW-{iXmS;3g0nOvVKo^cW)TXM)Ao?}dfJkz!x5Sap2V^@r7B1)5wh3_ zn-a}QHPU|YCe_BRrjq@$ z+cNvTn#Q1SKk3+s8!v>!MQD)u2QYl_1GAWV8}Q=%oF%dO7A7lTSgFW`-EEUq^6^M= z!9IjsA7BF?;Njt~(D6uxoG4WdIMOpak$wFr2(k^lf;f0XgG+$9l2V=7!!wFK;!&kK zI!RsRPR;x@<7}+@5!=Ikg5N%&*MFIYIC+I}P`kIf#4ZUf>Tl%iy8c>r%W>h?9j4h4 zjb(bHo7C6PI25aa>l0dLkhFtro7L8}y(z)#t0IV|YSdYcs!RY+|GQn|uQ)JHT!ctv zM%Z-$HVR_zzyhc?L*1%>Tnj~;pfc=VW|`VA?%72!{V05rsKj5&#`Z_F@^`Vl7s@;x zCDWy@EgWsPKGsSwB&#rvv*IGa5p>u8E>6iZ2u-3&)NfljZ z^L+p{Oc19+OGkj*q+2QEEg?^=oD2dDlXPV3aqgW|q~7`eBI1N_{u@8+>R^TgF1ys( zB&C;<{by46T$LsoS~E0hB{;OOd;NaYSM90)D)NsU4By$4fK0=q=L}85H-3@NM3+}o za;|j8&LYLmEezO*L@4(yBqnhRA6jlDdiPVn08z-eU{WJrj?Tsb&O_uljZD#Ep;7e% zuhpo_!m#Sb@#{ywL}Tf~eW`k?=Nyz(K+#lQ1N6w>j ze;W8}qB3B@kT;@_TYJowc?WuLMm+$DJE)m=;Y$dQwXI@E)8bJ(DU-xF{BN2xy11TR zA^BqtOss5%O9KmB5DXd;d}T*R7JV{`L5$2oc{YEuSfAQ7d?`+B>;TD9X@+`$BQ`aD zbX0CGWjsADcZz6A2F}~4&m(N!W|MipC#UqiiXG&^^rV2uC}f&LOmW%B*|rzev576 zaep7VvT}Ri`dylAXDEuwwzOPrQWCXtvG;Z>W!cJmZCcprEV)ZZ_F?J-8)XEzAcUji zeU-;Qi@)rMn``0yn{^99|Hqo-nTm&rh%t^kbmg{r1+vYZMOH^kqrZ*d4iwg01@=Vkk)v`~&c zOM(FR1tM>X>@`3oskLn`HNb#T&;6X7q{oF*8DaV{e;?2je;mQm|Jdr>#8hp-8s*Av zD(?s4`J%I2e^~jsD$2XdsHTcjx7n^iuE6HM{jPwL=PYQsteQ-4>6K=X^XYUy+`2FV zhBeg}PKbL*fpLn4x(be>Vyu6&s*y6=%b&QNb$}Bj$U#mt{ z+Jps0ff}*p=Z}8l@7}Du6CnCA5o)|% z?>ft0eR6CRwmv#4eYByZ$*{yW&+~6Ul{O=@*Zphvqb;dbCYyVsecg_B*At=3aehSL zXF%C8SX=RAZ8ERf(R{l;&DODmdciWlg>Hdpx^kPzCYZSK5-)21O~X1s(=5UbQ?g5< znJ-4;HVyVc&^ z+|j`(A|t4PS>>GJg=$EU%b^!4&~F@mc1iE*^?o5`KqcYJHt7lm|FL%hnZs-TUr)*n z(H5m&DCoMYOw&a$hNh;ba?(z6Z*D8*S5~s|;gHl907f5&iI6V2#`4T_NY_i~IVE!N z_G)rcpr0JkGwkc?>NFYCs2yYeJ{oc)Y|1DBPB@odTUvz_cD{X@ZQeTQ1*XT`<29yQ zrItP1BX*g%{?FX8nR(@eGy4qiw6hmns|8P$lIhPiCojhTnP?`jNMARJrx+mR@rD4y z2lI(IF{)q_mkL0hhi`~4Zm{l z>{I9HuZWyorAg88t-WLTXoNMT+nK#7e!l{;LAaH@V0T1NE#s?<2%0-Rek(xfCKa@Z zm^4^zc;X2NuakRID+ivVvnCmtm=tm=MbH@OypMR3ktePsR-^as@}A&!JK2zk>TzK` zQk|vO+lTvCKjP+H1liH>XiOs`xzVOdB7wZ@O^{Jcb>ahL7&fD@5Ey zzmPc(E>uf)B}CR>iMj+ZMP1I72l{EJ_Kk%jsa+{LRhN8D4(1I@r$Yu z=?+e>Tb|0+1w}vRbh3D!h*vZW7^RktR#Rx$Y_jxwIPhLw>itzzV+obi!VL7g^uCq^ z6k9;})5s0}Ts$E8LMhtOj5!!4jZ;0JJ)QzPqma&$MzXWQ6}-YJ4QIUE0%aau^zAn3 z-`2y-?M@4^>FP>Hihq5Y71nF5yN|9G{0JR;qy3DsHV_ z>FC|Ab@>GQpWbDBqU~1SVj$oLsE2E~YQZ^En)xbr_D}BS-3|oyP0ZKLt1b)?auaD& z&2DqM%6fW1dxV$l6~9g7^UW&3VyS8cVrm}6F~rW48n z`-ch{r3bdNNZUwBNNSG+Hko8{5-vqf@LC)@Y9JP}9cHy`!Xw3&EI=3WDVdD#KAchF zs@h7zh@3CZ!vs}?uN|t%L+ria77v#o9O7A>JuF&S`!xR{?trg0T$+1#Prl7}GZ%U-#K63BTk^;ridqO!X(^c$5B-<6U5RkdYG42N>=zhFpdd(@dptcvK|f1vmVVC`Q9{S;yx|TSGT#GDm)Fe z)lNK@1UxA=8E}?QJe%6ORGbpSzHYHvxb; zR;=?khck2*{H~H%-*d7k5q%_=Ab5Dm^;A#`W36Jf%Q@#7>WEas_!x3)Mb9u z3`-lf+u0hUy-(EhyqY5tNuhKkVuR6e7&6zygYJs3sThKyEEmidSM*`~*BkN?h}S|l zb?SnH!C0@jCCr+uC0sCr$4^eeStt6P=5=Q+NwBRjZ4jb94pryX>A&V-1=Tg{5znCm zFTSgrs#f|D_(l6_0(QNiiMB4BA7KO0N+wp*kw^Jjw3^$D=XPJ(fNz)a&4WDd-fu*# zHMZ2P9k~9obD5CI()9rJVUm`K)RS$bEa^`_$h7y8Io^C1H5QJh9enaeJY8Of^|+IU zY%8F&1c)UErTSv5@yw!l6w}(gyd_F25dNEv)hF#aFN^y7p2c^k{!MtNzhT4!xGl+h z6nM_?_gUu3_k-(Kzh!(54{%o(UC#2Shxk8_k{%x*_kOw(jD!k^xN@NwFS6^Vne_2j zTcqCoNwlr0T}2%|NWSta)s3HLZT9(ab{y(jjg*Wg=&2ZhZtxcSQ%>p>AV_uUSx#G) zTB`a|Y7R%`&`@<6Hm`fQhRt>^FyC&YeQ^%3IS4aRvWz&@pX0a0Pn^d0X6LF0)Jx!LAAlw!Fgd5;5B)Z;}lMh`F+jiwfGC${T{$LczE}S&%h}G(h|m2S2>CyM2uwPc+G=we5@s;P-hiYmJbo(Ae#udX_kPB{oZ(4o4;iynwyiv z_+kF$HrSWsGU!6g@6^ll&I+}gg-SEv)^w^jX9Qa>4Xd=IyE~sUQ0(b0efzxA#x^f0 zZtwAI9dt%~;C}mk$7hRf6L4j_5{3-r8HO1?Y)jw6P=r|$HC#$Gg8cPP@8)Z&`)hn{ zNN?Rkjhobi7&*;1z(rk-iqUv(%V~clRZ$UvzU$&^NOjG^E`y{$6!tzb1Nnf@L#3(h zS0HQvw7BAYTo3cFGxQheEHDAwg?!f4GH>m&)4zSis+7g*rV5>ynt*i3_@s!Ry97LE z`)0&36>QEfB0{?Ye2HIm7ir@mtBLv57b0tviyjc(b|uJ!(n?oWEgn*O&ULm$C#=G!NJCeC?|Kz3&1S zS7$<{w}=xdYIDKs&wtH5*OUDBqmZ^;j;`d?#hsv>I}ZUS$4U#V5dkk-L@7b$V*z`!~9V*JD znd7)>J{V0!zIQt0&4>8X^N>(=Z7$*O2D>kh9|Rdf953-;(6O+by`C4YT@40Iq$y*< z7RDt^PA zx^h)fx`CoCEDsb6Pe{yMy&>&zHWJ!x3AXFks`osYcW^Lb_VzUXYC zz58h*&R%$_kI=z8a`i5~p65ZG;jwUBF!CDj5~GG5((A>{%y}l7wdaskOBRjGm4}T@ z8M4@UiLwO5@lws-#h-#wC{cX|czI9tVGYZWk@>0Hz0Mt2ZftgZI-g)Y+DN|w94H3+ zsd3Ehzv8b4H?b!val=1|NP?C&i=Eg?OMl&=C*bI!b_B=gXH;b zDf;F%z-qxU%-2G-_GVdcTzR9h&FuzKwzP7#WaTn1ui?2xK~bMBR^Z+HvQ?6o4z?Mq zRtbzuv(PGk?rb9M|LJhQSMvLu;_ezE=HE}*wu?-seh9lU?;U+3K*@w8#UBc!PM*ZB6dy zK)?Ds%H{7h1_oNj8a=&uJpAHG{{M6ADb5N*6!PswqvIf^J7gnPR5 z!0HEgscLh~rj`&pJsK}IYwPJ%4+-_f>hh|7Ees-PJhtkF@oB%~nH+Ou*2PcKW<|K~@Ez`byZSjD(9 zJE16aH_85)@?`xj0=S>Oc;S%dnnc$bFsk0?4ysNA#5m*YG*@>SAL3Yk!s%k1Rqw(SY7M^sXKqd}~!6kPlsf2!w%bhNa_ zQNX46I_!iN6&0*1+ip0RS+-tPv`;y(!1|zAmMJtJpwHNd&_!vFW03suZurZ_L`vXk zx%_>(GZ}^~g=vp(;2=nm=}Q)6D$CHX!h6+6s>@)9w;K12Op>GT|ScC{yxODU3>i`X8Ro}bp+rz)WJ;};qm1;TGxirmUrLy8q zVR6ujuZy>{mqK;0AFTgP-P5l5)$TO#p>lbp-PWb9O-+>6MzyTWAv#c|u<|eL`cZENQb!Wu*rrd4bW|>lzCz1l9IcB>zj~N@5;Y4# zpFFi%@Oa!TJ!HKG3qJ(XlO6cw#EdYEKjVbPjz$m;K})< z+pb3~V=4GjB}k*otEKH~FK{8U4hnpp9@|N6&*6PPMWIiw*?Z|hB4PkpLGY@>ZJmY^ zaHgZN5@hskr9}!wqUte)ut@0$mxx#ie2%{4b#nczD@cj(9xYvMY_IMNS z{*kwvrTOLTwyJF3gd!!njpU2YQ6qH3s?IidF zm8&>0n;4BBE%b7D%66a1BCST46?DHNzKTwa2 zo!*h%L1**;HXnoPzOc@`%rsuaGh^ivHnZ!04L4q;LAnJ=e*V@ZsuOI_Be=mSt3tYy zu0@gl{`kb(*FxiF<8_kAViThtbez{?o@eWlyfL$>7u;kEpJJSF4~ou>-PnKLNXDv$ z>Rwd;GJx_fZryxx=)rq20BB($AH_Ij8Nc%v&}}bh%^UQzy?hoNK<_glq(MyfMRtVP zB8bm?Qh@xGp1IlAQ533GySH*V)<}U{a5L4 zdI)Epd>ujGp0;l;`&{;|3cXlLoIPZS>|6P=!%iWHJpHWa>8A+W=wuz0@09`cgPOWx zIG7^j^oj=+B7lzpRfqP9eKp~9k1z1(2MwL77asNa4e6hcC9D*QjJ{ltRLGez*ozVG z75-%Jjr$e}XqEMchiF=5W(dPIp`0ULb?Sr_wQvpTsI*yVXx9v-8bUfei)FhQNN?bT zTlxQZ!vMpXXn5!$3PLMK>HN)jV){cC#q z3@z^I_MFYH(Rm#TdmK-r1!#6hc38l3`rfA9Z{i_<=@aQe7rSYuAwHJespky{yqbrJsA-)jhNqFY``TsB)eG``d>nlLXPqFNphd$hb8S@Y?!bvcUTBk~!{p4<9O z-YHg0Y~p?hr+tCBA2h2S~@Pl6UZu9**@ag=Y>_VJhZKp9F2L{cdsZ%0YVveqPw&_PUUJ7i> zvgYy+fLnzh_WIAmwF|4`D~0~K%es@eD3*}P!M3lG#xf~6n(#@%v|aTK@nG>qd;xNg zlLZpTaC~)wC@)q7s8DX-t+UgSqb}?Nn|Qh{mE~{x)=sr>WpK-}H>Z{M7R4*r(US2g z3SUc%KCg@WL+|U;60g8*yZ>SxVT9q!vh`v&7Sk_SU3G<6*%klL@j%tjoKZ$A4)QTn z;;7me+9`&~YMR6@WESR(135YMA-b4DnkTlWJl`=_VCP(MG%czZ;NfXB?l%-2me8ia zd)3#0zU(mSS6;gbGK1ZtUygszc8-3c79Q8&k;=vp{-cleB$Eqg#hfHi9kBxiwrnsZ z__q+{wdLH{Gm3T;7%WqW(KgbF8?n!bkfiXRNqvcjtBN*4M93M&?n2GRjWV^#-P{xe zdfYl+gk2V71cUyP63%jKk99xB9`75}@o@z}QS%dtBcY;3Z1Pl}>_MMq7hCioF1}~Y z9_kN2PTjm|&qEP=J7E(>M)Rh%x&Bl+v?zy4@J81#l%l@zYe?if|2F_SW!sDMiK~J2 z7TdT#s_Us{nJj|;RJqiXRe>}TrP+Xy3Y*(^!|5Ip^-=fqG_IVovL|`wai@Q;_moS7 zH$CjkTd^ivnd$D8ai=rKel8>gYz)0TCiRsT^(^#`j$`LrsOc$!XZWGHaoSyq&mb8G z?jJ>-V&64~|E98NnN+gtoR}*4lkC=`RA0=6hJ9`hj*iy<{(K3;XWKvj;jzpzT|-&h zav{_q8LMi)I}B?p&!4ZVkFxu&>G>av^#!|rfn5MEgc!HdQjMi9d`U6>1O7Vj88Y z-D$n8i>aa=$LYLebNP}rIy3Tpn{7bHtUt9D5B*%YiEeZ%#+rMr?(Jk|d0}5kuD)6u zZtYiC?|@Ay9?fqOG~YQ-wI1(wi2iIG*<+GCfqS3D=k)=UC=1aWF5h#B;-tJ4AzppVgimx6sg9mmzQ_45dm^dg+y z1YMa5(YFVst&ijis9A)2r`jZYL!u(M^MZ~AsId1{r+?>FJ9b@=ji8ckBAs?XMnTi+ zJNa#YAsFrM0j%p#%{p)zBIcAM&HUXNF> zAsqiatZPd+%q{$>IaD=ub?W9bwH0R#=buw9;a2Q$g42Y>rX7vtbs`nXpa3wNrX~k6 zIv5#2(gd)m?(WU7kcf4ohJB=`G;#!olN;^#{>CHV#XK?coi{*?#9aI!?A@to{XRiu z`tUv>au@S?wQjk%(+Sc0NO27z;~OMPU6m5=Dx7LV!iFD}lZn)5AxKn$iE!g_dKyNa zZdd&V{zNBntg2qpILFvIQa;@R=tMwL2NqMgl>R46Un#zx!LE}|Rn5iLYM6exA$^;{ zB`Gn3-)vo|va#i^!YBSqIrrh@AETmB@V~jHt@i-p=6ROTIY@;@c>K8<|J_q6ZiRL@ z4qox+Y6E@UmaP@7yL<1@DI#p^jJg!opqKzKqj1mI?Ir+9@ba3MsNL21cH4g_`pEsp zPB+_9)@urWe0)60ofbCO7_=Xk5FDgr6Bz(lJZga%8`_CR;8u#`#^1VM4#Qb6i&bZv zKQMS%@ohhKzKJF$>yMWW1RdWnO!qHkiDRY9k$6dJeJ90Bd=x}w!lg^DC_j{-`(5zo zaU%q)_2?i8P`i#*X<^Sl^A3-zk>Jq-8!FBn>ClUD8B&lX%Q|g$(7c$^S^0hc0&}$g z|0`09aRsY0yJBfjZDewuMI>u%xp+yki*>`&?-vu)7ljfNq{FUe?DOO&LK(w@4u;f* z#K#kNIPb8GPZjA4k6+#I9jh^y*y`pCU?YZnyk-}uuTxa8h@8CpZD6qm2eUQ`LXJk* znnWN74x|lg*%yx?>Xo5QTqPGf#^p~o0XC%_OkDBL`J{j%X<@sS0nUgB4IhQ;7?NoU zH<>%J(BJ=zju-->Qi!y2l~89P=m4BX#_>R81H*|56N9Gau0Xr9$o;ks*<2P>CCT{W z{kTvjXUh8mZ9>HRTsg#Iz#pl@B2|!>t=T9=v5HhfMoWyI$cgiRV}1~Ry{|IUDR!R3 zyu#iKXZ~ABtf3_XQT!GAP-v8*PZ%hU6Cb5kq)^E!l3eiX#-~qLa8j5&)xt1nQImb_K zwjcKiU=X_;;8^}f=VOE_i(od}U!02xcp2}T+&8w5t{q(n3|%^G_n zc@?>Pu+fetV2hI zUL1D1$f!v@{HFe`VVgN?i5C4t`tuXIllvMn-?2P<2N~C6snx%ebgRxVP?o zp`p9l9-06&Jfc#3qx$EA19`sP*_ijVfUD8WSCM!G zrRUI*S-4HBSFodprq2t%_wiy&2EX)9hrc>!?|n(8kpo|6Iy!Wa9wY}Xo82sW-q^dIZ{Cb>D(?g z$xsGIcb3XDvGn153XP-wnRxC^@jr)#T!^CDP$`)suUii%anEVFbMh4*C00(_jOXa~ z()lOHJ$xZ4N`qQc|1DX(UDx5z`*1DWYyW_U17@-=6l`=cNUb-yL637W4I%Id4tT{1R2V~b@nb{qwUlG;=jS_p}ImRyc{vSB%V$_0B z1FL)Q^&u)EI!uiL|8`#UYO!!?uf=N?zZbWKR;9jP$3F9i;X=-jOSL;h$wFVZH3OCY zFSI&>142SbABxB(nRy0Be4Q>Pww9)-| zs?iT64p=j(vcgP zzoP#`Ls27R6*~|9&5m*Abuhl~OeEhMTjtyCkuMpe#Yg2CSH+GCCHXO1qalYb9b6qx zOon>86(p`XnT``C83cwJ)zlkuD!~K!E+)Oxkihc5Ku*B_5DkL%-K)?+0DC47DH{>4 zGTqz|z*-YThJ%twrno!b&P}i0+5yM@k%*wwcO$5|dOw3TCtsF}FHLh3)PC5jlM3sM zj~g|F5KcZ|BKT`L%QRC#^1B*JxCRzi=H}sHySDxWiXk=IF5&OnB~pe2n9_(4XRr=r zJY}RBTYbKwP`!E-&EP{bjuTn6D8na!Zpf;BmY?rg09WrZDr{!YW*+BAi56#{0>{}lRffMowpf4!5Vj=6 zqTy(_tCLzJUgM+IEp&VGi5}Ob-{p9?2*teUhnf;kzum6_r1-ch7ecF!HC;!DF5>2H z7-cIf(dW!_AjSX%udyX0dv4-m;8iXA>YI2kcy36UXJ9a_H$IR69VmDEAIE1hiOIfd zJ}V_1u9IdNzIK~l%d`K7sy6qDdhX0kJ%xoPn+*Eo*dk%%ZP?2&f-hjhT0r069#fM* z4xAdEpFg_r90y{P{_kVyeB&w;iQz5!&q5?k;e7zt<+DR(snEbV#^eH`&GDb;osq6za7LTA1Xb_?} zQOv!sw)6Y_(SDx}HnF*_=+tB_oVGyJ(}a3D$Ed#n_AN)WOxr*e zkfiC{MjkV00??U-A0!yy(JLrLp!vtoSl4xcaR?`FbVeBJE%J*NI}%bL1-k#F4Bn}v zo`V-{3n&;xJv)?-{*8(^)+C*DNXb=y^o5Nrp;4Vhs^m#)hZl!eSi!!I45RCRJ~_2P zvFs<+(AEi}>dG`)Zn3>Idbv5hE(C&eP7varM8`fSS}9!C#Q1C-E>*A>s*w=m$()d0 z*SDl*+s|Q?MkV}94aCllD!0K!|1Rq34%9%<8cEk zrq!dKFDoyu2c9?eHIS;lnsBI(<-_3c8LOP8G|{LHq+k-c31Y(CKPdg4{{dFWEL0MO zo0=dkEbKc(4{;K0FxbYlhxJ9a-S7EuIusS9gac1D4#Q;=kaaUw<5?Jgm-cFUJjuYjeo?6Nl2{6Lr|kcWXZ;8|Ly^C zN{_=hd&frmu-#MR!^wnXkYsqEFQ|s|1@>{w4-O2ZZ`a?|qY-hGlctug&32gB+H+K) z2UL0+Jw%)Enl3Mjv}_aKW96%H501+HJaxP)GWhSrc290Nq_m+PltpvaCIkDh;N3!Sy>@R^nNp+iPmV5&x^>Y3=09) zs**Vo0=ILO`CqQ3W$)~4%FIz7nZ>_UlE1w$$RM zy{FX_X>~~G^@I07mW9#l(!_-|Oc!c?&$xFF@b+UrB8O;2P7}Cw4Xh~7uW0`}A^me1 ze5K`8lQ@LaZnm?uvIdP&LbM^*@3v5bhmybj9ruyX%H5fSn;PCu zK0Ja}QYR$oct-2c$h%M-(Y^7}&5<&cwiEU*BXzk?!zoIQ_ZqA+IlgsbuIM51_W{wp zomQKW$4dd?ckd8^Kg?<4L+ofIAyDOu`w&=h70`I&exEU7{5~2SkMtoy2JxpWK59S_ z{x<~%R?)jQMs~J@$VIxOx5_m-O>#-nhu8X05S)%0fZuhBcT+J^w7(7Kd8Bw|D?$O&h@ZE8ub5B z^_F2#wd?yZ9YZ$~Lw9#~3P?#RF*MTM;0)a%QqmwwiFC)%DIqQ4ASvA;g75P8-}`y? zyN>z97mhV^$90|88C2M4?!v_zZsSa%*B@DOitkNNAl|nMRbOFXbavuKBN-1$RB6-O zJ{_1m^mFy40PQSMf-iP;kjM8>{|l!kZn=}q>{3KQ4JCDA)D!#sfo<|kqj~hfG0Yit zWll-i%odkmnU^H0NoS7l`^)Dv-j@E`q&oIRQhB^eh?bJFb!cF4=_$-neM|j;P zy+pzV4r7<3jUgA2pDV*FI!OaPibvwGhdcgKsb$iiKr%E2hvG4TyrT>@5e~8}L%-wI=ZwTZbuVro zB#MQ!CU@e0oM%)`N|xv^FZZPEZhORZHZOP9YL*e-mKfqALw&2bD>FE;|Mi?!CZYV$ z-Cj_&)cl1pjgTPf{h1}VqA`4m*godBnN7ECINf1T*9Nx$cKUAC-1Ni**gsvedGT1*6{{`&C!jMgtZY(l~EFNgap4i6fH9)>%8@ zFC7sdScNX#CSJZ$NI*VTt1#o&>5C#s62$+7=)`_fdk$Nyf>#wGMm3mslv&gDHaRoo z^i&@Zh%kxoxq-#ClcxhrX8}3IMuaI*rmx-OcX3!|mY3tO3HTXU-fv&UN7n6^Nh2y5 zxL<-44Uzh5-CNB7Am%@xv;6^5WO8iPP@o;R$e(%v#Rm0RnS3%(K?dob-zmGY*vNGR z%v@ulR9<|0o4w3t>}N=74T_0i+RYKuS1m_koqCJ zb2Dtp)8Sa<74$~;(@@_-=wY&Bq#+mCyyx0Hk6cm}&t!IeDrR-(_0%zmmX=-GT1{siY?igmZufudjQQLX% z5cdo08Eia(v|>Oz7<}FD=d6WcBrRTLK(2M~p_?QXba_?t^$igdQ;dn=@+dGO%2ccV zqGHGx*Ce9FO+=bq2v`$sZNmnwmjz4n?vE%NT+`7dHl>MyoJaJ3UhZsvxSrc;nGDym z#kD8Ju*#I-Q)unN!32BmkFktS!#uu(kpkpQXyhDr(41FA8YweZzKh#2_uFyowmf|Q z*I1L=_mn}TW^b&4|2{B=3BVvOxI=R^jsz`6vT%IA5IRgXQY80VWS2;$#-*QqSB%ql z=i-t`%3b(`AXO^EbMBr(bU;lAl)HsfnqS}*jV-K4lZn4EYtbqUQiyQ@ml`~iCQzUtxjRusC-e45uWb0m2ah!Fafhk2g+G&o%m0=;40b`Ur(?+@a z#>u@AbofEdyiK^3+B!)Y1ZI#wLG9ke*}XB6hDTfuy?8(SYQYSw=fq+2s!4W2&jU!; zgHK1OfuBJ3^#0P`Kr%pUv@N*sG4Hu19^TRBS-@LZCqHJ#G5>TKgqg;+Y>=hNt+Qyf z1}#CgKacuD`GQlpN+`zRwXG+9^RqjGf22tk6(*VJtHLe$m~5Z77OR@0*3m@trgmLR z^~uQY;#Es4+bvqX9?ErDm_OZNh! z@e5zSq2<0lMi74Lm!0`m2MXiEPCk0Tr~n#3qu-0fMZOg!S|Xj_?Uc3CU}P+QT$Q!o z$Dd?A=ydzQN~$UHKecZT>6kCpUCW7BbCrk`b8tK=aw96w!g8)44^9_oA6IB+(Mg-Y z>(dLXsw(kDwE5(0XG`A7o`(lierf4)7q{w=(~Q3Np&V3X05#qUQz%ZN7>K>m9z=DZ zlv+xmlj|Psqgy<2j1dbyY54$x)Zk5hGgN5>ci_uR#}U3T#2${2Kf=8T$-EEK52P#7 z^#Ibji~lD70J%MC$RG>>F<1l>C1esMuriDDdTVP)^w<0IdLU}$)OSl=Zf_d34PQhC z%vJwr5n&^!^xF*3z7Sf!=>UIOtedX1 z&+<1vtK

    qSrkl*XpZm!)mbTJD-qVF@Iu$v?)7KktC~ol%!3`FT#{{8G4nYO&Ey) z*H;s0G3MSgs05&Da>P=?XJtqWFuAss zTFq!-h1jy*!71VTYxR;p1T04Fdz*QT-fp5iO7^e%tLx~bDqzaLK4HK&e>#F5?{(BQ zRz)w`m}0XBG^UX4h`~WN>IEiX>dr^gPXMF@tWCPJiNOvvjUD+yl!JFtS`JHdS5uov z(9BN4*E)z>4@D2;Sf4uXwj)^iNi`_pR_3fkgD5g2m5h`Q#onYvYQ3s^eF7Ps$V&K) zCbf0H8aOW@K~$Skc>+3eG8~VOCq33|=`l-UJ$Kbj?eCr+JoWQeD#P#-B-)iRxuPe3 z-o)!#Zc_F}8dA!;TLrZS^NPA)pgWJ=XsU={kN=-ExBbP`m`)Y@K z`gexKkg(NefnRhzu1$#@V)k%-4<^O-u(6=P@bMRC@UJoLS~3~JmJweDv|ka7$UE#F zXsC`@7y#*7OiUQ9TEAkcv=q*7tl)lfuso)rt)IKShOe>11aWsL$U_6bpU z!PH{OjJN}>eb+mlrH2QGZvPVn?;4M~ZoW5=Q1hn&)_#*<(7TXONwzVVBy+l<6s1dgn5u}}YDa9I5~@S2zq2AP1+c|g&dhEO1rLl+Ix9OvuHE2QsYogf-ry_qs1p&ZvC_ATW)?!(E{!2UW^$}mFQ+qr~)E|h>PdZ}=gfksn9{CjKG18$^-D#H$o#wXF> z>KgJ-`meqmA4!j}QSiR(me(E7Wj2|rFhfT&ZS^RIuU~#ZvlVQBT4CQ`z)Li^_iXfq zbyuEf;MMw2xtLSUU@K5&`Rxnr0P0>stVRgEC0h{@<8+?<^wEZY%yLoy`C+@-+7-?8 z2Il&kxFkg^YBB=RPVBkf0KnY9o752yv@y;v1#0clH=)R7$F~5p$DPC#?k3)I$rrMXR6Y)LqlKPwj;3N%F#KvoLr@%L1gNl3W20W zVldv6*eiAdTFH^TNTOGJ`;zeMTi9ZyRVEBK@;>75g(%LpmrAw$f^zM#!yT8(%1Md< z2mW%W1+kx=uBhTa=wJyp1MbdE44#m+M|F^{sU^j7RHrLCs@6VQYDTlYo}c#pZ{)Oe z4aJK7`a0HI&EHRUzkaX-g0VfgSph`5Hsbdfhzrk-GE~AuDf1rJW$kJp-f?35>^R)F1vlq>V1EM9#H(U69}mc{ZEry;IZ> zwUcl&Hxok{b#*)RVdu}`q4%6)N(0}g2nMfcArRhaudq>chqTe)o zdk|Prp)dMxmx@Mi#bSD;l<*f#eNGuzRT<;eRZK$=BwGjHG^@Y?lZh#&v{-R}UZ$X6 zZx@Alv>P?mz^C6@O{j>J2*Fc%o-&7Q)ON%Ia>(Dl6eve0Jy7|pVwqFvK?IdTZURX6 zLNH?4Kvu?ws_c-(!ksNv4+}~BACbSR2=J8%I<1kT=^X2jB!We4O4R@?2}+9pPRt7d zy^xiBX9rY+rp(6gt-bej6R#4J6?wph_ZtV;vB~&}pivDX*hRPFh&n|)MjscL>Jy0dwZVsSz}YKmz=_;^3C_^-!YeHC}zPFh+P26PhLC3Ub*p^ zhyXm43ztXJ$2#N_kl86)XMNs5hfdv=>WU9Xi$Zu zMp4PVAlygt3!uQ<<%9!cxyBb(ui|mW`&uP@#>|ag!fi^Y!ia3C)KCJY-7h~Ni@=(=_#PP4vM~J1J z+Bkz?3;!GSFGM8Bf-j4b6F#gN4u2;!e(w!#H$nu`416$dTcoxvpPEQ z$H$THK7YjQC-AZcs1V?!$obaAqa%^ZDrY}-N(c~TBmX#6hT}&Mwjr8*dofo8d7cco zhy4fFFUg>q+h^%L5UI=Lno}YSkJlqB(@=STt#KG z?X{+Qa?{{|Z0{ft(rx^$aF(CrBT!jq6v}^Vp$sZ&`JjXkfc)Rn7HUInS1*$J+}UJX z3m(D257En?4rIK=_V9ch=e&K;a2A+3fh9{KfPj_g(tpo=i|4+F49~7)iBn2H*D4WH zpg%=Yn@8`$HZsbyjm}o^iXa8>U0k5*6YJf-Koe^n1 zbRJ9VS?LORi~MK53R^u@kYD;x{}Povh~zCj;c0_ZN4jdY97dgtz+UPsrwFc$y8L=i ziOYfEYi$Eb&6+N$kB|Gd(f1djmC}sSG>Cw0Ll5%VJ00jXqlYv|8|NtQJ9`oq^$&P! zN6KbFD(dMLfdQQ~iJQ|rs@WZRiL%YsXu2dW@~*EJ+1|sT8CrX1gfwDE@ElE8gIQ9m zyhlk1_3-vgk6H$s^BheBCO_#-_~9dghRV(ru8_L*Uhjyyj?GiRjHDej{d!6v`6&xM zIOfey5|h5Z`rq8ls@qcYv+3hit@s>~4cG4C+b(Kcdu2{ThzVnz!B`zYw*dO7AYzCc z$|Wb12}#YXANa2Kexogo9Tb(NqTI_%2{WMqBMVX568jCZdQZVcjB;Sg+--P>v{l(oIX{w6a z-mZtvZ+dU9D=d1E0NBOviZh6GbsPheE<9u%TclN72Jf#g_+Jl^Z>8Wa>_(Gms``|| z8WxUb zfyc=Gwt6XHuJwK{PSR@&3&N2skVPYRXtT^WNc;w^txk zDF(kbpmRd?xeOB5ceST4a#me5vtToFPQU)!b4HxAuR@K2q(^T~VQ=q@!+1arQm0_E%LR2k&C68tpz+|ch<25MOafitEU;#!i{ac-4G{P?9 zQy?yumt%^3^^_?p{r`3egprtZ*@OhY0{Q6^qXxHwDony_AmKcu8sw~BZtn3c-ikWF z9Zr*{LqG6QkeX0g$bgp3c@EudQV`T)t}hqV?EJ2)mfG={myLH7)ZrJxM+5EW$s z(oQFRTzP^1J03k~iI$_NiQBrt1i_OMD|~pfR=+}nF8L(fh;xoyOq6@|EZG?L{0zZN z?Ot=VBn|fi~dD`c}%lOVBq{jfQcq|qvQUU?~ioO4#325aTD%ZSdeE-vttv_Ft z{RxWY+Y9AE1WbMeAA`vc0MvFUc!J#+Tw~sQ)XF||-tLGMHoY0uZf?#b%PyrSR_O4h z!ob9w54zdCNQMCMx0jsaC~6R`$60`JXO&|03t-O~%jy=fBhJ|2V#SpNqS&Z-Qz+Zo zhyZf9TN^_PN|&dH5a&LglvEv&S5X(u&`UE$?R?4YxPJ8xwt+x^uBetQKC7mNe0@~4 z^nx2J${IySkX49=a?gKpV6v|&kT0)Mf_i}c&7HKglCX}}01fXgb2NyeML7SA(|ArMd&xRofF6`L`J8`!G6HFw zsV|xz8gNdG)Ik&lw`-9AQH|nW-6l=M2leO(@{j}g(Iuy&d6w93U}nSbjvSsrpvw=Z zOse2uyVzvsL`TG;y*${1q;9D@II<$k$s(^(#aA<64>#@vPKSy%|!vH58G zS!Z!Kis*5en4{mlh?3{$Hd^%l5gyX}gYou3p$@QmX$`vKoS6L4N$}Kg!yGRqToVTe zMU$x@sv*g>#5A!0eO)l_TZ6oRcZz#m&-!F=zRsztBEE13Z_3^;k=o28!NqZ8P5N(> zzp0Bc*PrAHiuFaSAq$F#u=vbJ7>q5q41;r@Us$#gBSJ&Xoz_ivwUqpC*!!#Bz~ptm zkff+Ru_(X$7QePp(?D+-CNzW^CO5wA`Z69BxO4rw22|khEB}fk|KCShNTtJ`x^ptr zKGLvCVumgyXM#FZ7)^zwK8O`m)q=( zP3>_nG7IsgG9%tgGd3aR=~8(%Vda%&>Slt1)UXYFu{THo(x4Z8cy4ZjN`U-Hk*0YC zKZsWR45PKsA`N0|`<}>Vru{8BPM$Qnq|jDu)DI}Wg8XzT0?~_lzO}}++g75X?bAwO z?JL6OR7V_9V;F15g-fETzKxXVrSg{3P>&NO8>n`p8E{L-c(~k+T^0Vfh#*1khirIY zHn^XPMc zmt<@)X0msu281$xFw%=PmG6*!_-y?=7VF#Oo*{x(3?KX%1(*io2g!g4b>gAaUR(gw zjk3Y>8A^r+)cG|n3Jn+tJSp+-`WJdJbdWuTj-)RWc^g3NVe2}YaqDw(Pkk1lMCJNE z(_QpB0AZ)#)k>(Tp&Z{)M48W1w#Itn?>A;Mj>0O`S<2Pxbk*sqo<l}T2SIBhjd?mPpSk?W;qQ4#`yL|fkj#!!ozy8JhKCen) zYmnA!lZmZ!YZ*!-vR}Y39CBiD$=#Y|wT)W6Nuu{ zz?KuQNPn{bCW0sCD4KrH5o#Vxb{K#R_-{4xGKsc5iTxxxn>}0=50tV41Cf-})ECt@ zS`W|4SY;H4*deb5O4Tg+cK?Kf(D{*+BlIp*CV zPw?OWkZoWZQrMU0s^UUwo~jmH!Ftyn%ZEt$^X1Q-d&<9#RcD;`=^PzAC*qGx?w=^Q zoMr6|L4Hm91kpVF3kRMJN^=X-Q_z>ix)aHV4KBE^`3!lei`1xEH4)z_$YD*@SQtW# z#BuDXPohx75j(7@f4fr4N^xvF8@FUxT0dyu?^Acstz2JhjU_hw_{?z4m3`o(YH$w4|}vQ zBKYQ4rcahpsgpg!esgcbO`7-wDXhpg`k#&4f?A{bh`-Cbo!~nwY|?F%1(b@4US@Ew zKCBYGS9>QXTN@rxhFn(B#h6VRIb|M^Xe@w5| zZ|QDw^deoK;jJ-pGPS#BLaT*fzZ%`M(*`%im<=no`%NJKJU2I&WPTqy#!*xS9(~-m{>W5FzT8VZ%I;TKjvEznFZ{@C+e-g zYwRI|H(xYkm1)p+ku>MU{96>qr7hUPPhvdak;wrb#$5;Qe`?ge>o*)Y`$}1EKkAAc zU#w{PiST7UTonW*Z3mQvsSVTC8j$#jXEr-$V0rfu&{%J`cXj6Ju@LKV1L|zjt}eA@ z?g>S8{C?z*zmQo)l~cmw;+|$KS;l}JI}K5X*KTZpAe76y3lzDOU@it1W_};Jc%S)Av=~{gbby!jLPQu`|XCh`C0huZNaDZwuj(Qs0VW%~tLV(Nhoir&wchw{( z%L)<)sewiY1X%2^&p(HqU@x%*fX~Y~t7|A2NKX~in32MI0{bccT_ypZ3LSV-J{M(@ zZ?}V)w_*aAvGOdxy9M6|(}m(-W|C9`qzOwEUdtO_3|BpN)2rtHf-a{Ob6OR$aNSHr1zs_`zv1c?6_qmLKtAVCcTHjt4Y)QQT=q^*Q=g z)jS;yI0fJ{=A|uSqwo0#>QX8B(#xV`9Mto7$S5 zCu(GVB!c!%8Bwz-p;$oi`qRDJR+L*R3g= z&>SMwEuaL!!c~|C(6eI9GX2zC zdtDmYkSx8^Pos8H#hDhiGBWu)IOAN5WWryn4IL=MY8zW8%fd-(ouvnqd4A`Z2aqnE zn|$V}>6h2RqzoDUnEcOy(OM>M@AAp5_qRUzuVBeu2RGMQnUKSX@8p6LhfYBpQ?2F~ zgHng`*)L7>_>8U>OJ^wGq8P~#&u$Y@9L`@A$-9Tw=N1+LN`p6}mCu*y$w_c`H?>U%gU}qwv?@ADf$0)$z ztda`A9*kke%KsIDZ(5Mat!p?zWT}Ppq|CP-Wau_}uqcsE(JEk)|n;sryoCqB5 z0zHn)vag@s>UUZ5S~h!mU(|B)+1DXKws|tK&>4v1_{sT|*{KAqC`ilBx5S=t5dRBe zpT+kINQHht?k&>!=9l5R+g#OGvv|N*a41;!LjkJ_Tx}nj;Q&~tJuf&IRc2F5E7IhF zMLYAo-?qd}kqZG5Ec`bVVOD~VAHAPOXAnSwY^as$g??kvyov>SEOVj23AfWsraj@6vRpJ=^xi|*`dy-9D?3|8h#ML$T;(V|BL_7P|c`+*3)Rz(*n zhHn#{l?~_12_DT7n zG_PR`fn4fCKP0!W=Bz!|L+PysOYF^+SC5V#^)52NGOq5dwO_0xif>>zAkJ0h&;T##Y9X8Fan_wu_%X)S|Qq z|7aEBE-D)#-RbKbqU#QW~s8?vOq=@_LjPNeo& z<&OT4LO+^2V=`Tx)fe`G|81s1;&%vkl1;>?Jzbxk*Ac_pKf zCLc&@P{lY;D@lFqn>TGq>ysh3())a^i#FK)_@G+_#K9VU)Xq(j#42hmSSCY zy}3Z~#>8qKB^4#ahJRs1rwAzhR%X_zocNEsZLF2*MtQ&2uDK5YS~p)orHvw;%S`H_ zhuJcsuFWpYgaWh|-00_zJuF zwp7YJo4kZrB5cg%{W7jG8+zTF2B@I}m-mIylQAfTS?oJIoI0+Qf3fW!8A-DjT@TV9 zejD>;M(TV^mZlNrJUDQQo1X~kddr&tOb2MclL&GqypjSZh$PN=J*s1m*7UII)Gm&o$$8ijVzp9xwJ}OUR^Npj1mlXISl3$0coDE zW#Pr6F&7!(HG~rr{?N6sAj+QLN1WLDDJ&H~wlYL9tcWUAzbJq6|D&UQGHMB5or(zS zK_=|e4~9^F;1@LXygL`6RwC%zps(V7;b&}2T;JQdbU=_$uptsl#n`};o2kj@o>hh z#!1r^skXGEMQoU#qX*!GevM_hEQ)D(W=L>(y~Z3(E%iQ~Qj$@@dp-Qj({538Wjk5@ z^bNDnTw^TyGpQedZ0`zi)wP`7$BL##NbRsqe~!cch=Ky>_9$3I2c}FO=&2q`sKSoh zC|snM=bcR5youINqZiLmF^_G}OiaQi;j}Zg#JKHRlNYHm8S{x|HRa7b84R#d+)!D$ zuzz=G_^E#NHaFcZy!_t9EgK~t#J&6z7hSgieco`~qNfx8j>|YBiR#kR?&ac}{4sJF z|KnF_8e~hMH!QXeH|f@%0v{^D5MrggWGhSpRxr5<7-+^D)e%?Rjzl1X)?V>eYgpEf zatt0Hio2V_5{nc$s8MjEU5MX3Ni~TilisI7+4G#4pRIVKbsDo&QyRFfQ})bbwC!VA zh&9U+K?)uk%mq>1-7Wvm?LeoD*ojh8XJTSY1%SrN_TSLPMVhW#lIDATYq;R@Z8#*E zQGZ|esL1N~@@hks!Ez7_x%&PU)d65t2|#`G z=&2c^9P)fVz7Xqvi@$OMz*(IiSBgDD1Bff}XuB`b(;JguA*P$kdlfKODh~zhF+6@X zrWYbbg{#)crHCV<>0+OS!xGrcq1q6n4!cD#x+^iW@G6ez02+(6It(g8%-edByAV^1 z$xaUVn{{x3y@w-cK`c~iq*&6xTm~4nYq8GwysxT~s6%_Tw0W|D{#qWAh-F`N_>3h0 zkcI+MhYCQhPJW;y2003xZr=$+oqdb(_4!j3#jojotKF(EQ(Q@?FN!6e#x_n;ijx#oZAO6WJ=UDH*V^a%#i#!y+ zfiLfM*}n~zu<-k=o!r!oy?YLx;eQ5*k6<)#p(*Zpr3@&bwI)nyyF2%?qwZSY-Pzgx zDA87c)&baBXRPHXC!R-O;+J1pzAz#5cpsPxUr64X70kW*&R~MFn!vH2cX!l1mr;gdM4Y$ z_|Y6q%RXCMj>TuMH(jTo$=EnQK{S^uo>yJ5%g72RK_6|z9(l`9_}Qcq3<==DuI>uu zxT;Ya2n-Fd{-cnO&7u@ka?8uwSw)XPX4Q&NDSMzCAlgW!vgGmZ?ldDBw0^qkYrHyQ zzsn8f9oexUtU8c@kr6~kUwaZ$kOE zkOomg77bCUMH`PJ{RdGb6xvR0D8qBf;VXgP4I&ZtG2p#h42&;!3C~CNrX+pJkJj zL;_64Gc8S^HOg!_YJJc;mNu%PWCMEh$wkIA{(n_6KUfsus`HZ8Y%ZdnmhZgTq_UGt z=;5xj_Re+=tAD}oZLTF+$XS3{@=vJK`@Z_NaHx;QHrvQ{&C=g_%wM1IN=!ghqR%W& zFV#sg7&d=*WFQS-^=%sxI>UGDN@;LxIS-es=O4A}h{O(&6bQmeYSa{9KYxx zDk5ubCd?;RMwAeMCc%OMmO;6iufLAQPPHeo)q8faJ5{x4=r%LB0y#*boN3}RYizq_L=|&go%(lgEVT2LI}H-5keZw@N}A$FkR?f# zH@;@v1sQ6{nbWS7*ps=UT30if?B*f=SB23cp!#S)IvSI`P_M+VA@89t3LpsAW;O9n1voq^b-u z=$waOP396Y7#A^cykD{-xvHfEYce{e6>3zLqA}2q7)OR8& zP*Sz0o16FvYGZ^j7*z>2H(JzL&OQ+{?PATsQ4p{UH=&>4Ddnv^XB7z{{f}tbQ|fue zH(lOE`H^8oYIM|=;e(ffa8`>ptVIl!m!$W$&nE4r1Y>kZerGDUzzHz`?EY=-FzPn`aKD)r_@OBE@T}-XQ;}}90zD+$@*9fQHI0ziL>8DBd!TZqKE0kdiW#bIYaCkr z%;3FwTXxe^=2olsscfGWBps9(BMZ&g6_ zH6X_<^QVVguyE+kg{V8L$#=2p9v z-V(IHGso(yxT-Cp19Y0HaNhJkcMK`&rjmDHW_SDhafS3KG~s4T}Vr zs+u9MVhW#;(emSv5GoKeU=Z{Q?nMH@(|tcbl9&?Ok7uGs9b2@cQ=a2V)8<*VnV9X7 zxvWXTpB6$Vom1G0*h3nmt(*FKJEd$|zH(c`^Fqw{>IY?iu7+6~+3~t?Cd^-RGh3_c zp)UxlkWNpptT^))pBV`>^%`-8kAw$wCg9=e%}3Cm97OWm<@`_Vgn2=-wsTk&MFoojoN~SRZ{=+1e6q)I=7hdMv&ks3?=dkG(2Ra^1~Gra)*!f&H^R; zl;l$G&(V{4&ZY#uuYMsfe0RU&6-dTY_%+=zYcEIGdvU%_Uoh&rEFlfwLW#NhHiU

    d^F7l$yW6Y;oPlx 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ⅇ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 { )} {!isPlatinumOrTrialLicense && capabilitiesFetched ? ( - + ) : isSourcererLoading ? ( ) : ( diff --git a/x-pack/plugins/security_solution/public/overview/pages/translations.ts b/x-pack/plugins/security_solution/public/overview/pages/translations.ts index 55dc208ec11c..d4c4e2b17d11 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/pages/translations.ts @@ -53,7 +53,8 @@ export const DETECTION_RESPONSE_TITLE = i18n.translate( export const ENTITY_ANALYTICS_LICENSE_DESC = i18n.translate( 'xpack.securitySolution.entityAnalytics.pageDesc', { - defaultMessage: 'Entity Analytics features', + defaultMessage: + 'Detect threats from users and devices within your network with Entity Analytics', } ); From ab901040249a6a5cb0e5e47694cbaae4a85ee64a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 30 Sep 2022 20:35:58 +0300 Subject: [PATCH 169/185] [Lens] Displays the Explore field in Discover button if the capabilities are correct (#142332) --- .../field_item.test.tsx | 31 +++++++++++++++++++ .../indexpattern_datasource/field_item.tsx | 3 +- x-pack/plugins/lens/public/types.ts | 5 ++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx index 3c6ddbe5e849..64de29b0691c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx @@ -54,6 +54,11 @@ const mockedServices = { getRedirectUrl: jest.fn(() => 'discover_url'), }, } as unknown as DiscoverStart, + application: { + capabilities: { + discover: { save: true, saveQuery: true, show: true }, + }, + }, }; const InnerFieldItemWrapper: React.FC = (props) => { @@ -460,4 +465,30 @@ describe('IndexPattern Field Item', () => { ); expect(exploreInDiscoverBtn.length).toBe(0); }); + + it('should not display Explore in discover button if discover capabilities show is false', async () => { + const services = { + ...mockedServices, + application: { + capabilities: { + discover: { save: false, saveQuery: false, show: false }, + }, + }, + }; + const wrapper = await mountWithIntl( + + + + ); + + await clickField(wrapper, 'bytes'); + + await wrapper.update(); + + const exploreInDiscoverBtn = findTestSubject( + wrapper, + 'lnsFieldListPanel-exploreInDiscover-bytes' + ); + expect(exploreInDiscoverBtn.length).toBe(0); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index a91286881197..6434af979028 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -370,8 +370,7 @@ function FieldItemPopoverContents(props: FieldItemProps) { [indexPattern], getEsQueryConfig(services.uiSettings) ); - - if (!services.discover) { + if (!services.discover || !services.application.capabilities.discover.show) { return; } return services.discover.locator!.getRedirectUrl({ diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2ccc393a0932..29aff3d42869 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -494,7 +494,10 @@ export interface DatasourceDataPanelProps { dragDropContext: DragContextState; setState: StateSetter; showNoDataPopover: () => void; - core: Pick; + core: Pick< + CoreStart, + 'http' | 'notifications' | 'uiSettings' | 'overlays' | 'theme' | 'application' + >; query: Query; dateRange: DateRange; filters: Filter[]; From 659251bcb6884cd33319ed8fdc34d439b5927936 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 30 Sep 2022 20:36:16 +0300 Subject: [PATCH 170/185] [Lens] Fixes the Search functions input on the formula documentation popover (#142341) --- .../operations/definitions/formula/editor/formula_editor.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx index d742b0497a85..1e9a304ffa8a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx @@ -845,7 +845,6 @@ export function FormulaEditor({ anchorPosition="leftCenter" isOpen={isHelpOpen} closePopover={() => setIsHelpOpen(false)} - ownFocus={false} button={ Date: Fri, 30 Sep 2022 20:36:31 +0300 Subject: [PATCH 171/185] [Lens] Fixes the dataview picker selection for multiple layers (#142308) * [Lens] Fix the dataview picker selection for multiple layers * Find dtaview from dataViewsList --- x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 e72253f6e2a5..c381d519f366 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 @@ -357,10 +357,14 @@ export const LensTopNavMenu = ({ ]); useEffect(() => { - if (indexPatterns.length > 0) { - setCurrentIndexPattern(indexPatterns[0]); + if (activeDatasourceId && datasourceStates[activeDatasourceId].state) { + const dataViewId = datasourceMap[activeDatasourceId].getUsedDataView( + datasourceStates[activeDatasourceId].state + ); + const dataView = dataViewsList.find((pattern) => pattern.id === dataViewId); + setCurrentIndexPattern(dataView ?? indexPatterns[0]); } - }, [indexPatterns]); + }, [activeDatasourceId, datasourceMap, datasourceStates, indexPatterns, dataViewsList]); useEffect(() => { const fetchDataViews = async () => { From ff6406fee196955d98bdf4a200514f4c21575ead Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Fri, 30 Sep 2022 11:56:30 -0600 Subject: [PATCH 172/185] [Explore] Risk score entity consolidation (#141318) --- .../cti_details/host_risk_summary.test.tsx | 78 ------ .../cti_details/risk_summary.test.tsx | 96 ++++++++ ...host_risk_summary.tsx => risk_summary.tsx} | 67 +++--- .../cti_details/threat_summary_view.tsx | 16 +- .../event_details/cti_details/translations.ts | 67 ++---- .../cti_details/user_risk_summary.tsx | 85 ------- .../risk_score_doc_link.tsx | 2 +- .../components/risk_score/translations.ts | 48 ++++ .../hosts/components/kpi_hosts/index.tsx | 4 +- .../entity_analytics/header/index.test.tsx | 75 +++--- .../host_risk_score/index.test.tsx | 113 --------- .../host_risk_score/translations.ts | 65 ----- .../columns.tsx | 35 ++- .../risk_score/index.test.tsx | 131 ++++++++++ .../{host_risk_score => risk_score}/index.tsx | 134 +++++++---- .../risk_score/translations.ts | 51 ++++ .../user_risk_score/columns.tsx | 69 ------ .../user_risk_score/index.test.tsx | 115 --------- .../user_risk_score/index.tsx | 223 ------------------ .../user_risk_score/translations.ts | 65 ----- .../overview/pages/entity_analytics.tsx | 8 +- .../risk_score/containers/all/index.tsx | 88 +++---- .../containers/feature_status/index.test.ts | 9 +- .../containers/feature_status/index.ts | 13 +- .../users/components/kpi_users/index.tsx | 4 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 28 files changed, 608 insertions(+), 1056 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx rename x-pack/plugins/security_solution/public/common/components/event_details/cti_details/{host_risk_summary.tsx => risk_summary.tsx} (50%) delete mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/risk_score/translations.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/translations.ts rename x-pack/plugins/security_solution/public/overview/components/entity_analytics/{host_risk_score => risk_score}/columns.tsx (58%) create mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx rename x-pack/plugins/security_solution/public/overview/components/entity_analytics/{host_risk_score => risk_score}/index.tsx (62%) create mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/translations.ts diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx deleted file mode 100644 index 2320b85113c3..000000000000 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { render } from '@testing-library/react'; -import { TestProviders } from '../../../mock'; -import { HostRiskSummary } from './host_risk_summary'; -import { RiskSeverity } from '../../../../../common/search_strategy'; -import { getEmptyValue } from '../../empty_value'; - -describe('HostRiskSummary', () => { - it('renders host risk data', () => { - const riskSeverity = RiskSeverity.low; - const hostRisk = { - loading: false, - isModuleEnabled: true, - result: [ - { - '@timestamp': '1641902481', - host: { - name: 'test-host-name', - risk: { - multipliers: [], - calculated_score_norm: 9999, - calculated_level: riskSeverity, - rule_risks: [], - }, - }, - }, - ], - }; - - const { getByText } = render( - - - - ); - - expect(getByText(riskSeverity)).toBeInTheDocument(); - }); - - it('renders spinner when loading', () => { - const hostRisk = { - loading: true, - isModuleEnabled: true, - result: [], - }; - - const { getByTestId } = render( - - - - ); - - expect(getByTestId('loading')).toBeInTheDocument(); - }); - - it('renders empty value when there is no host data', () => { - const hostRisk = { - loading: false, - isModuleEnabled: true, - result: [], - }; - - const { getByText } = render( - - - - ); - - expect(getByText(getEmptyValue())).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx new file mode 100644 index 000000000000..a02c7729f4e0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { render } from '@testing-library/react'; +import { TestProviders } from '../../../mock'; +import type { RiskEntity } from './risk_summary'; +import * as i18n from './translations'; +import { RiskSummary } from './risk_summary'; +import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy'; +import { getEmptyValue } from '../../empty_value'; + +describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( + 'RiskSummary entityType: %s', + (riskEntity) => { + it(`renders ${riskEntity} risk data`, () => { + const riskSeverity = RiskSeverity.low; + const risk = { + loading: false, + isModuleEnabled: true, + result: [ + { + '@timestamp': '1641902481', + [riskEntity === RiskScoreEntity.host ? 'host' : 'user']: { + name: 'test-host-name', + risk: { + multipliers: [], + calculated_score_norm: 9999, + calculated_level: riskSeverity, + rule_risks: [], + }, + }, + }, + ], // as unknown as HostRiskScore[] | UserRiskScore[], + } as unknown as RiskEntity['risk']; + + const props = { + riskEntity, + risk, + } as RiskEntity; + + const { getByText } = render( + + + + ); + + expect(getByText(riskSeverity)).toBeInTheDocument(); + expect(getByText(i18n.RISK_DATA_TITLE(riskEntity))).toBeInTheDocument(); + }); + + it('renders spinner when loading', () => { + const risk = { + loading: true, + isModuleEnabled: true, + result: [], + }; + + const props = { + riskEntity, + risk, + } as RiskEntity; + const { getByTestId } = render( + + + + ); + + expect(getByTestId('loading')).toBeInTheDocument(); + }); + + it(`renders empty value when there is no ${riskEntity} data`, () => { + const risk = { + loading: false, + isModuleEnabled: true, + result: [], + }; + const props = { + riskEntity, + risk, + } as RiskEntity; + const { getByText } = render( + + + + ); + + expect(getByText(getEmptyValue())).toBeInTheDocument(); + }); + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx similarity index 50% rename from x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx rename to x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx index c4380550c422..6f24803165ac 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx @@ -12,41 +12,52 @@ import * as i18n from './translations'; import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view'; import { RiskScore } from '../../severity/common'; import type { RiskSeverity } from '../../../../../common/search_strategy'; -import type { HostRisk } from '../../../../risk_score/containers'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import type { HostRisk, UserRisk } from '../../../../risk_score/containers'; import { getEmptyValue } from '../../empty_value'; import { RiskScoreDocLink } from '../../risk_score/risk_score_onboarding/risk_score_doc_link'; -import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScoreHeaderTitle } from '../../risk_score/risk_score_onboarding/risk_score_header_title'; -const HostRiskSummaryComponent: React.FC<{ - hostRisk: HostRisk; - originalHostRisk?: RiskSeverity | undefined; -}> = ({ hostRisk, originalHostRisk }) => { - const currentHostRiskScore = hostRisk?.result?.[0]?.host?.risk?.calculated_level; +interface HostRiskEntity { + originalRisk?: RiskSeverity | undefined; + risk: HostRisk; + riskEntity: RiskScoreEntity.host; +} + +interface UserRiskEntity { + originalRisk?: RiskSeverity | undefined; + risk: UserRisk; + riskEntity: RiskScoreEntity.user; +} + +export type RiskEntity = HostRiskEntity | UserRiskEntity; + +const RiskSummaryComponent: React.FC = ({ risk, riskEntity, originalRisk }) => { + const currentRiskScore = + riskEntity === RiskScoreEntity.host + ? risk?.result?.[0]?.host?.risk?.calculated_level + : risk?.result?.[0]?.user?.risk?.calculated_level; + return ( <> } toolTipContent={ - } + riskScoreEntity={riskEntity} + title={i18n.RISK_SCORE_TITLE(riskEntity)} /> ), }} @@ -54,26 +65,26 @@ const HostRiskSummaryComponent: React.FC<{ } /> - {hostRisk.loading && } + {risk.loading && } - {!hostRisk.loading && ( + {!risk.loading && ( <> + currentRiskScore ? ( + ) : ( getEmptyValue() ) } /> - {originalHostRisk && currentHostRiskScore !== originalHostRisk && ( + {originalRisk && currentRiskScore !== originalRisk && ( <> } + field={i18n.ORIGINAL_RISK_CLASSIFICATION(riskEntity)} + value={} /> )} @@ -83,4 +94,4 @@ const HostRiskSummaryComponent: React.FC<{ ); }; -export const HostRiskSummary = React.memo(HostRiskSummaryComponent); +export const RiskSummary = React.memo(RiskSummaryComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx index 485f36b0416a..b3ba2febe5d7 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx @@ -26,10 +26,10 @@ import type { TimelineEventsDetailsItem, RiskSeverity, } from '../../../../../common/search_strategy'; -import { HostRiskSummary } from './host_risk_summary'; -import { UserRiskSummary } from './user_risk_summary'; +import { RiskSummary } from './risk_summary'; import { EnrichmentSummary } from './enrichment_summary'; import type { HostRisk, UserRisk } from '../../../../risk_score/containers'; +import { RiskScoreEntity } from '../../../../../common/search_strategy'; const UppercaseEuiTitle = styled(EuiTitle)` text-transform: uppercase; @@ -161,11 +161,19 @@ const ThreatSummaryViewComponent: React.FC<{ - + - + + i18n.translate('xpack.securitySolution.alertDetails.overview.hostRiskClassification', { + defaultMessage: 'Current {riskEntity} risk classification', + values: { + riskEntity: getRiskEntityTranslation(riskEntity, true), + }, + }); + +export const ORIGINAL_RISK_CLASSIFICATION = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.alertDetails.overview.originalHostRiskClassification', { + defaultMessage: 'Original {riskEntity} risk classification', + values: { + riskEntity: getRiskEntityTranslation(riskEntity, true), + }, + }); + +export const RISK_DATA_TITLE = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.alertDetails.overview.hostRiskDataTitle', { + defaultMessage: '{riskEntity} Risk Data', + values: { + riskEntity: getRiskEntityTranslation(riskEntity), + }, + }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx deleted file mode 100644 index 5cda8c903dd1..000000000000 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/user_risk_summary.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import * as i18n from './translations'; -import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view'; -import { RiskScore } from '../../severity/common'; -import type { RiskSeverity } from '../../../../../common/search_strategy'; -import { RiskScoreEntity } from '../../../../../common/search_strategy'; -import type { UserRisk } from '../../../../risk_score/containers'; -import { getEmptyValue } from '../../empty_value'; -import { RiskScoreDocLink } from '../../risk_score/risk_score_onboarding/risk_score_doc_link'; -import { RiskScoreHeaderTitle } from '../../risk_score/risk_score_onboarding/risk_score_header_title'; - -const UserRiskSummaryComponent: React.FC<{ - userRisk: UserRisk; - originalUserRisk?: RiskSeverity | undefined; -}> = ({ userRisk, originalUserRisk }) => { - const currentUserRiskScore = userRisk?.result?.[0]?.user?.risk?.calculated_level; - return ( - <> - - - } - toolTipContent={ - - } - /> - ), - }} - /> - } - /> - - {userRisk.loading && } - - {!userRisk.loading && ( - <> - - ) : ( - getEmptyValue() - ) - } - /> - {originalUserRisk && currentUserRiskScore !== originalUserRisk && ( - <> - } - /> - - )} - - )} - - - ); -}; -export const UserRiskSummary = React.memo(UserRiskSummaryComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx index 340f63e71bc7..3a74d4614d99 100644 --- a/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx @@ -9,7 +9,7 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { RISKY_HOSTS_DOC_LINK, RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; -import { LEARN_MORE } from '../../../../overview/components/entity_analytics/host_risk_score/translations'; +import { LEARN_MORE } from '../../../../overview/components/entity_analytics/risk_score/translations'; const RiskScoreDocLinkComponent = ({ riskScoreEntity, diff --git a/x-pack/plugins/security_solution/public/common/components/risk_score/translations.ts b/x-pack/plugins/security_solution/public/common/components/risk_score/translations.ts new file mode 100644 index 000000000000..f1df3145fd97 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/risk_score/translations.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { RiskScoreEntity } from '../../../../common/search_strategy'; + +export const HOST = i18n.translate('xpack.securitySolution.riskScore.overview.hostTitle', { + defaultMessage: 'Host', +}); + +export const HOST_LOWERCASE = i18n.translate( + 'xpack.securitySolution.riskScore.overview.hostLowercase', + { + defaultMessage: 'host', + } +); + +export const USER = i18n.translate('xpack.securitySolution.riskScore.overview.userTitle', { + defaultMessage: 'User', +}); + +export const USER_LOWERCASE = i18n.translate( + 'xpack.securitySolution.riskScore.overview.userLowercase', + { + defaultMessage: 'user', + } +); + +export const RISK_SCORE_TITLE = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.riskScore.overview.riskScoreTitle', { + defaultMessage: '{riskEntity} Risk Score', + values: { + riskEntity: getRiskEntityTranslation(riskEntity), + }, + }); + +export const getRiskEntityTranslation = (riskEntity: RiskScoreEntity, lowercase = false) => + lowercase + ? riskEntity === RiskScoreEntity.host + ? HOST_LOWERCASE + : USER_LOWERCASE + : riskEntity === RiskScoreEntity.host + ? HOST + : USER; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index 5d1b0f58da6a..3a732072f2be 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -14,7 +14,7 @@ import type { HostsKpiProps } from './types'; import { CallOutSwitcher } from '../../../common/components/callouts'; import * as i18n from './translations'; import { RiskScoreDocLink } from '../../../common/components/risk_score/risk_score_onboarding/risk_score_doc_link'; -import { getHostRiskIndex, RiskQueries, RiskScoreEntity } from '../../../../common/search_strategy'; +import { getHostRiskIndex, RiskScoreEntity } from '../../../../common/search_strategy'; import { useRiskScoreFeatureStatus } from '../../../risk_score/containers/feature_status'; import { useSpaceId } from '../../../common/hooks/use_space_id'; @@ -23,7 +23,7 @@ export const HostsKpiComponent = React.memo( const spaceId = useSpaceId(); const defaultIndex = spaceId ? getHostRiskIndex(spaceId) : undefined; const { isEnabled, isLicenseValid, isLoading } = useRiskScoreFeatureStatus( - RiskQueries.hostsRiskScore, + RiskScoreEntity.host, defaultIndex ); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx index c7e7eb411d4f..b60264b39eae 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { act, fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; import { EntityAnalyticsHeader } from '.'; import { Direction, RiskScoreFields, RiskSeverity } from '../../../../../common/search_strategy'; @@ -44,28 +44,31 @@ jest.mock('react-redux', () => { }; }); -describe('RiskScoreDonutChart', () => { - it('renders critical hosts', () => { +describe('Entity analytics header', () => { + it('renders critical hosts', async () => { const { getByTestId } = render( ); - - expect(getByTestId('critical_hosts_quantity')).toHaveTextContent('99'); + await waitFor(() => { + expect(getByTestId('critical_hosts_quantity')).toHaveTextContent('99'); + }); }); - it('renders critical users', () => { + it('renders critical users', async () => { const { getByTestId } = render( ); - expect(getByTestId('critical_users_quantity')).toHaveTextContent('99'); + await waitFor(() => { + expect(getByTestId('critical_users_quantity')).toHaveTextContent('99'); + }); }); - it('dispatches user risk tab filters actions', () => { + it('dispatches user risk tab filters actions', async () => { const { getByTestId } = render( @@ -76,21 +79,23 @@ describe('RiskScoreDonutChart', () => { fireEvent.click(getByTestId('critical_users_link')); }); - expect(mockDispatch).toHaveBeenCalledWith( - usersActions.updateUserRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], - }) - ); - - expect(mockDispatch).toHaveBeenCalledWith( - usersActions.updateTableSorting({ - sort: { field: RiskScoreFields.userRiskScore, direction: Direction.desc }, - tableType: UsersTableType.risk, - }) - ); + await waitFor(() => { + expect(mockDispatch).toHaveBeenCalledWith( + usersActions.updateUserRiskScoreSeverityFilter({ + severitySelection: [RiskSeverity.critical], + }) + ); + + expect(mockDispatch).toHaveBeenCalledWith( + usersActions.updateTableSorting({ + sort: { field: RiskScoreFields.userRiskScore, direction: Direction.desc }, + tableType: UsersTableType.risk, + }) + ); + }); }); - it('dispatches host risk tab filters actions', () => { + it('dispatches host risk tab filters actions', async () => { const { getByTestId } = render( @@ -101,18 +106,20 @@ describe('RiskScoreDonutChart', () => { fireEvent.click(getByTestId('critical_hosts_link')); }); - expect(mockDispatch).toHaveBeenCalledWith( - hostsActions.updateHostRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], - hostsType: HostsType.page, - }) - ); - - expect(mockDispatch).toHaveBeenCalledWith( - hostsActions.updateHostRiskScoreSort({ - sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, - hostsType: HostsType.page, - }) - ); + await waitFor(() => { + expect(mockDispatch).toHaveBeenCalledWith( + hostsActions.updateHostRiskScoreSeverityFilter({ + severitySelection: [RiskSeverity.critical], + hostsType: HostsType.page, + }) + ); + + expect(mockDispatch).toHaveBeenCalledWith( + hostsActions.updateHostRiskScoreSort({ + sort: { field: RiskScoreFields.hostRiskScore, direction: Direction.desc }, + hostsType: HostsType.page, + }) + ); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.test.tsx deleted file mode 100644 index 4f4edd279a56..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.test.tsx +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestProviders } from '../../../../common/mock'; -import { EntityAnalyticsHostRiskScores } from '.'; -import { RiskSeverity } from '../../../../../common/search_strategy'; -import type { SeverityCount } from '../../../../common/components/severity/types'; -import { useHostRiskScore, useHostRiskScoreKpi } from '../../../../risk_score/containers'; - -const mockSeverityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 1, - [RiskSeverity.moderate]: 1, - [RiskSeverity.unknown]: 1, - [RiskSeverity.critical]: 1, -}; - -const mockUseQueryToggle = jest - .fn() - .mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); -jest.mock('../../../../common/containers/query_toggle', () => { - return { - useQueryToggle: () => mockUseQueryToggle(), - }; -}); -const defaultProps = { - data: undefined, - inspect: null, - refetch: () => {}, - isModuleEnabled: true, - isLicenseValid: true, -}; -const mockUseHostRiskScore = useHostRiskScore as jest.Mock; -const mockUseHostRiskScoreKpi = useHostRiskScoreKpi as jest.Mock; -jest.mock('../../../../risk_score/containers'); - -describe('EntityAnalyticsHostRiskScores', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockUseHostRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, loading: false }); - mockUseHostRiskScore.mockReturnValue([false, defaultProps]); - }); - - it('renders enable button when module is disable', () => { - mockUseHostRiskScore.mockReturnValue([false, { ...defaultProps, isModuleEnabled: false }]); - const { getByTestId } = render( - - - - ); - - expect(getByTestId('enable_host_risk_score')).toBeInTheDocument(); - }); - - it("doesn't render enable button when module is enable", () => { - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('enable_host_risk_score')).not.toBeInTheDocument(); - }); - - it('queries when toggleStatus is true', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); - render( - - - - ); - - expect(mockUseHostRiskScore.mock.calls[0][0].skip).toEqual(false); - }); - - it('skips query when toggleStatus is false', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); - render( - - - - ); - expect(mockUseHostRiskScore.mock.calls[0][0].skip).toEqual(true); - }); - - it('renders components when toggleStatus is true', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('entity_analytics_content')).toBeInTheDocument(); - }); - - it('does not render components when toggleStatus is false', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/translations.ts deleted file mode 100644 index a53ad8d55866..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/translations.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const HOST_RISK_TOOLTIP = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.hostRiskToolTip', - { - defaultMessage: - 'Host risk classification is determined by host risk score. Hosts classified as Critical or High are indicated as risky.', - } -); - -export const HOST_RISK = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.hostRiskClassificationTitle', - { - defaultMessage: 'Host risk classification', - } -); - -export const HOST_RISK_SCORE = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.hostRiskScoreTitle', - { - defaultMessage: 'Host risk score', - } -); - -export const HOST_NAME = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.hostNameTitle', - { - defaultMessage: 'Host Name', - } -); - -export const TOTAL_LABEL = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.totalLabel', - { - defaultMessage: 'Total', - } -); - -export const VIEW_ALL = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.viewAllLabel', - { - defaultMessage: 'View all', - } -); - -export const LEARN_MORE = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.learnMore', - { - defaultMessage: 'Learn more', - } -); - -export const ENABLE_VIA_DEV_TOOLS = i18n.translate( - 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.enableViaDevToolsButtonTitle', - { - defaultMessage: 'Enable via Dev Tools', - } -); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx similarity index 58% rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx rename to x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx index 998a356bf4f7..055a172e54b7 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx @@ -8,32 +8,40 @@ import React from 'react'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiIcon, EuiToolTip } from '@elastic/eui'; +import { UsersTableType } from '../../../../users/store/model'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; -import { HostDetailsLink } from '../../../../common/components/links'; +import { HostDetailsLink, UserDetailsLink } from '../../../../common/components/links'; import { HostsTableType } from '../../../../hosts/store/model'; import { RiskScore } from '../../../../common/components/severity/common'; import type { HostRiskScore, RiskSeverity } from '../../../../../common/search_strategy'; -import { RiskScoreFields } from '../../../../../common/search_strategy'; +import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_strategy'; import * as i18n from './translations'; type HostRiskScoreColumns = Array>; -export const getHostRiskScoreColumns = (): HostRiskScoreColumns => [ +export const getRiskScoreColumns = (riskEntity: RiskScoreEntity): HostRiskScoreColumns => [ { - field: 'host.name', - name: i18n.HOST_NAME, + field: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', + name: i18n.ENTITY_NAME(riskEntity), truncateText: false, mobileOptions: { show: true }, - render: (hostName: string) => { - if (hostName != null && hostName.length > 0) { - return ; + render: (entityName: string) => { + if (entityName != null && entityName.length > 0) { + return riskEntity === RiskScoreEntity.host ? ( + + ) : ( + + ); } return getEmptyTagValue(); }, }, { - field: RiskScoreFields.hostRiskScore, - name: i18n.HOST_RISK_SCORE, + field: + riskEntity === RiskScoreEntity.host + ? RiskScoreFields.hostRiskScore + : RiskScoreFields.userRiskScore, + name: i18n.RISK_SCORE_TITLE(riskEntity), truncateText: true, mobileOptions: { show: true }, render: (riskScore: number) => { @@ -48,11 +56,12 @@ export const getHostRiskScoreColumns = (): HostRiskScoreColumns => [ }, }, { - field: RiskScoreFields.hostRisk, + field: + riskEntity === RiskScoreEntity.host ? RiskScoreFields.hostRisk : RiskScoreFields.userRisk, name: ( - + <> - {i18n.HOST_RISK} + {i18n.ENTITY_RISK(riskEntity)} diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx new file mode 100644 index 000000000000..f5b9d0fcafc2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 } from '@testing-library/react'; +import React from 'react'; +import { TestProviders } from '../../../../common/mock'; +import { EntityAnalyticsRiskScores } from '.'; +import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy'; +import type { SeverityCount } from '../../../../common/components/severity/types'; +import { + useHostRiskScore, + useHostRiskScoreKpi, + useUserRiskScore, + useUserRiskScoreKpi, +} from '../../../../risk_score/containers'; + +const mockSeverityCount: SeverityCount = { + [RiskSeverity.low]: 1, + [RiskSeverity.high]: 1, + [RiskSeverity.moderate]: 1, + [RiskSeverity.unknown]: 1, + [RiskSeverity.critical]: 1, +}; + +const mockUseQueryToggle = jest + .fn() + .mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); +jest.mock('../../../../common/containers/query_toggle', () => { + return { + useQueryToggle: () => mockUseQueryToggle(), + }; +}); +const defaultProps = { + data: undefined, + inspect: null, + refetch: () => {}, + isModuleEnabled: true, + isLicenseValid: true, +}; +const mockUseHostRiskScore = useHostRiskScore as jest.Mock; +const mockUseHostRiskScoreKpi = useHostRiskScoreKpi as jest.Mock; +const mockUseUserRiskScore = useUserRiskScore as jest.Mock; +const mockUseUserRiskScoreKpi = useUserRiskScoreKpi as jest.Mock; +jest.mock('../../../../risk_score/containers'); + +describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( + 'EntityAnalyticsRiskScores entityType: %s', + (riskEntity) => { + const entity = + riskEntity === RiskScoreEntity.host + ? { mockUseRiskScoreKpi: mockUseHostRiskScoreKpi, mockUseRiskScore: mockUseHostRiskScore } + : { mockUseRiskScoreKpi: mockUseUserRiskScoreKpi, mockUseRiskScore: mockUseUserRiskScore }; + + beforeEach(() => { + jest.clearAllMocks(); + entity.mockUseRiskScoreKpi.mockReturnValue({ + severityCount: mockSeverityCount, + loading: false, + }); + entity.mockUseRiskScore.mockReturnValue([false, defaultProps]); + }); + + it('renders enable button when module is disable', () => { + entity.mockUseRiskScore.mockReturnValue([false, { ...defaultProps, isModuleEnabled: false }]); + const { getByTestId } = render( + + + + ); + + expect(getByTestId(`enable_${riskEntity}_risk_score`)).toBeInTheDocument(); + }); + + it("doesn't render enable button when module is enable", () => { + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId(`enable_${riskEntity}_risk_score`)).not.toBeInTheDocument(); + }); + + it('queries when toggleStatus is true', () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); + render( + + + + ); + + expect(entity.mockUseRiskScore.mock.calls[0][0].skip).toEqual(false); + }); + + it('skips query when toggleStatus is false', () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); + render( + + + + ); + expect(entity.mockUseRiskScore.mock.calls[0][0].skip).toEqual(true); + }); + + it('renders components when toggleStatus is true', () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('entity_analytics_content')).toBeInTheDocument(); + }); + + it('does not render components when toggleStatus is false', () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); + }); + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx similarity index 62% rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx rename to x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx index 9bb06fd261dd..b7796d442569 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx @@ -8,15 +8,23 @@ import React, { useEffect, useMemo, useState } from 'react'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useDispatch } from 'react-redux'; +import { EntityAnalyticsUserRiskScoreDisable } from '../../../../common/components/risk_score/risk_score_disabled/user_risk_score.disabled'; +import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users'; +import { UsersTableType } from '../../../../users/store/model'; import { RiskScoresDeprecated } from '../../../../common/components/risk_score/risk_score_deprecated'; import { SeverityFilterGroup } from '../../../../common/components/severity/severity_filter_group'; import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../../common/components/links'; import { getTabsOnHostsUrl } from '../../../../common/components/link_to/redirect_to_hosts'; import { HostsTableType, HostsType } from '../../../../hosts/store/model'; -import { getHostRiskScoreColumns } from './columns'; +import { getRiskScoreColumns } from './columns'; import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { HeaderSection } from '../../../../common/components/header_section'; -import { useHostRiskScore, useHostRiskScoreKpi } from '../../../../risk_score/containers'; +import { + useHostRiskScore, + useHostRiskScoreKpi, + useUserRiskScore, + useUserRiskScoreKpi, +} from '../../../../risk_score/containers'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy'; @@ -30,7 +38,7 @@ import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { hostsActions } from '../../../../hosts/store'; import { RiskScoreDonutChart } from '../common/risk_score_donut_chart'; import { BasicTableWithoutBorderBottom } from '../common/basic_table_without_border_bottom'; -import { RISKY_HOSTS_DOC_LINK } from '../../../../../common/constants'; +import { RISKY_HOSTS_DOC_LINK, RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; import { EntityAnalyticsHostRiskScoreDisable } from '../../../../common/components/risk_score/risk_score_disabled/host_risk_score_disabled'; import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; @@ -38,24 +46,69 @@ import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries' import { Loader } from '../../../../common/components/loader'; import { Panel } from '../../../../common/components/panel'; import * as commonI18n from '../common/translations'; +import { usersActions } from '../../../../users/store'; -const TABLE_QUERY_ID = 'hostRiskDashboardTable'; -const HOST_RISK_KPI_QUERY_ID = 'headerHostRiskScoreKpiQuery'; +const TABLE_QUERY_ID = (riskEntity: RiskScoreEntity) => + riskEntity === RiskScoreEntity.host ? 'hostRiskDashboardTable' : 'userRiskDashboardTable'; +const RISK_KPI_QUERY_ID = (riskEntity: RiskScoreEntity) => + riskEntity === RiskScoreEntity.host + ? 'headerHostRiskScoreKpiQuery' + : 'headerUserRiskScoreKpiQuery'; -const EntityAnalyticsHostRiskScoresComponent = () => { +const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskScoreEntity }) => { const { deleteQuery, setQuery, from, to } = useGlobalTime(); const [updatedAt, setUpdatedAt] = useState(Date.now()); - const { toggleStatus, setToggleStatus } = useQueryToggle(TABLE_QUERY_ID); - const columns = useMemo(() => getHostRiskScoreColumns(), []); + const dispatch = useDispatch(); + + const entity = useMemo( + () => + riskEntity === RiskScoreEntity.host + ? { + docLink: RISKY_HOSTS_DOC_LINK, + kpiHook: useHostRiskScoreKpi, + riskScoreHook: useHostRiskScore, + linkProps: { + deepLinkId: SecurityPageName.hosts, + path: getTabsOnHostsUrl(HostsTableType.risk), + onClick: () => { + dispatch( + hostsActions.updateHostRiskScoreSeverityFilter({ + severitySelection: [], + hostsType: HostsType.page, + }) + ); + }, + }, + } + : { + docLink: RISKY_USERS_DOC_LINK, + kpiHook: useUserRiskScoreKpi, + riskScoreHook: useUserRiskScore, + linkProps: { + deepLinkId: SecurityPageName.users, + path: getTabsOnUsersUrl(UsersTableType.risk), + onClick: () => { + dispatch( + usersActions.updateUserRiskScoreSeverityFilter({ + severitySelection: [], + }) + ); + }, + }, + }, + [dispatch, riskEntity] + ); + + const { toggleStatus, setToggleStatus } = useQueryToggle(TABLE_QUERY_ID(riskEntity)); + const columns = useMemo(() => getRiskScoreColumns(riskEntity), [riskEntity]); const [selectedSeverity, setSelectedSeverity] = useState([]); const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); - const dispatch = useDispatch(); const severityFilter = useMemo(() => { - const [filter] = generateSeverityFilter(selectedSeverity, RiskScoreEntity.host); + const [filter] = generateSeverityFilter(selectedSeverity, riskEntity); return filter ? JSON.stringify(filter.query) : undefined; - }, [selectedSeverity]); + }, [riskEntity, selectedSeverity]); const timerange = useMemo( () => ({ @@ -70,14 +123,14 @@ const EntityAnalyticsHostRiskScoresComponent = () => { loading: isKpiLoading, refetch: refetchKpi, inspect: inspectKpi, - } = useHostRiskScoreKpi({ + } = entity.kpiHook({ filterQuery: severityFilter, skip: !toggleStatus, timerange, }); useQueryInspector({ - queryId: HOST_RISK_KPI_QUERY_ID, + queryId: RISK_KPI_QUERY_ID(riskEntity), loading: isKpiLoading, refetch: refetchKpi, setQuery, @@ -87,7 +140,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { const [ isTableLoading, { data, inspect, refetch, isDeprecated, isLicenseValid, isModuleEnabled }, - ] = useHostRiskScore({ + ] = entity.riskScoreHook({ filterQuery: severityFilter, skip: !toggleStatus, pagination: { @@ -98,7 +151,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { }); useQueryInspector({ - queryId: TABLE_QUERY_ID, + queryId: TABLE_QUERY_ID(riskEntity), loading: isTableLoading, refetch, setQuery, @@ -110,21 +163,10 @@ const EntityAnalyticsHostRiskScoresComponent = () => { setUpdatedAt(Date.now()); }, [isTableLoading, isKpiLoading]); // Update the time when data loads - const [goToHostRiskTab, hostRiskTabUrl] = useMemo(() => { - const { onClick, href } = getSecuritySolutionLinkProps({ - deepLinkId: SecurityPageName.hosts, - path: getTabsOnHostsUrl(HostsTableType.risk), - onClick: () => { - dispatch( - hostsActions.updateHostRiskScoreSeverityFilter({ - severitySelection: [], - hostsType: HostsType.page, - }) - ); - }, - }); + const [goToEntityRiskTab, entityRiskTabUrl] = useMemo(() => { + const { onClick, href } = getSecuritySolutionLinkProps(entity.linkProps); return [onClick, href]; - }, [dispatch, getSecuritySolutionLinkProps]); + }, [entity.linkProps, getSecuritySolutionLinkProps]); const refreshPage = useRefetchQueries(); @@ -133,33 +175,33 @@ const EntityAnalyticsHostRiskScoresComponent = () => { } if (!isModuleEnabled && !isTableLoading) { - return ; + return riskEntity === RiskScoreEntity.host ? ( + + ) : ( + + ); } if (isDeprecated && !isTableLoading) { return ( - + ); } if (isModuleEnabled && selectedSeverity.length === 0 && data && data.length === 0) { - return ; + return ; } return ( - + } + title={} titleSize="s" subtitle={ } - id={TABLE_QUERY_ID} + id={TABLE_QUERY_ID(riskEntity)} toggleStatus={toggleStatus} toggleQuery={setToggleStatus} tooltip={commonI18n.HOST_RISK_TABLE_TOOLTIP} @@ -169,7 +211,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { {i18n.LEARN_MORE} @@ -179,15 +221,15 @@ const EntityAnalyticsHostRiskScoresComponent = () => { {i18n.VIEW_ALL} @@ -206,7 +248,7 @@ const EntityAnalyticsHostRiskScoresComponent = () => { items={data ?? []} columns={columns} loading={isTableLoading} - id={TABLE_QUERY_ID} + id={TABLE_QUERY_ID(riskEntity)} /> @@ -219,5 +261,5 @@ const EntityAnalyticsHostRiskScoresComponent = () => { ); }; -export const EntityAnalyticsHostRiskScores = React.memo(EntityAnalyticsHostRiskScoresComponent); -EntityAnalyticsHostRiskScores.displayName = 'EntityAnalyticsHostRiskScores'; +export const EntityAnalyticsRiskScores = React.memo(EntityAnalyticsRiskScoresComponent); +EntityAnalyticsRiskScores.displayName = 'EntityAnalyticsRiskScores'; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts new file mode 100644 index 000000000000..9ca0f659e14f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { getRiskEntityTranslation } from '../../../../common/components/risk_score/translations'; +import type { RiskScoreEntity } from '../../../../../common/search_strategy'; +export * from '../../../../common/components/risk_score/translations'; + +export const ENTITY_RISK_TOOLTIP = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.riskToolTip', { + defaultMessage: + '{riskEntity} risk classification is determined by {riskEntityLowercase} risk score. {riskEntity}s classified as Critical or High are indicated as risky.', + values: { + riskEntity: getRiskEntityTranslation(riskEntity), + riskEntityLowercase: getRiskEntityTranslation(riskEntity, true), + }, + }); + +export const ENTITY_RISK = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.riskClassificationTitle', { + defaultMessage: '{riskEntity} risk classification', + values: { + riskEntity: getRiskEntityTranslation(riskEntity), + }, + }); + +export const ENTITY_NAME = (riskEntity: RiskScoreEntity) => + i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle', { + defaultMessage: '{riskEntity} Name', + values: { + riskEntity: getRiskEntityTranslation(riskEntity), + }, + }); + +export const VIEW_ALL = i18n.translate( + 'xpack.securitySolution.entityAnalytics.riskDashboard.viewAllLabel', + { + defaultMessage: 'View all', + } +); + +export const LEARN_MORE = i18n.translate( + 'xpack.securitySolution.entityAnalytics.riskDashboard.learnMore', + { + defaultMessage: 'Learn more', + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx deleted file mode 100644 index 05f532617d5c..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/columns.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import type { EuiBasicTableColumn } from '@elastic/eui'; -import { EuiIcon, EuiToolTip } from '@elastic/eui'; -import { getEmptyTagValue } from '../../../../common/components/empty_value'; -import { RiskScore } from '../../../../common/components/severity/common'; -import * as i18n from './translations'; -import { UsersTableType } from '../../../../users/store/model'; -import type { RiskSeverity, UserRiskScore } from '../../../../../common/search_strategy'; -import { RiskScoreFields } from '../../../../../common/search_strategy'; -import { UserDetailsLink } from '../../../../common/components/links'; - -type UserRiskScoreColumns = Array>; - -export const getUserRiskScoreColumns = (): UserRiskScoreColumns => [ - { - field: 'user.name', - name: i18n.USER_NAME, - truncateText: false, - mobileOptions: { show: true }, - render: (userName: string) => { - if (userName != null && userName.length > 0) { - return ; - } - return getEmptyTagValue(); - }, - }, - { - field: RiskScoreFields.userRiskScore, - name: i18n.USER_RISK_SCORE, - truncateText: true, - mobileOptions: { show: true }, - render: (riskScore: number) => { - if (riskScore != null) { - return ( - - {riskScore.toFixed(2)} - - ); - } - return getEmptyTagValue(); - }, - }, - { - field: RiskScoreFields.userRisk, - name: ( - - <> - {i18n.USER_RISK} - - - - ), - truncateText: false, - mobileOptions: { show: true }, - render: (risk: RiskSeverity) => { - if (risk != null) { - return ; - } - return getEmptyTagValue(); - }, - }, -]; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.test.tsx deleted file mode 100644 index 6ddfd912e832..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestProviders } from '../../../../common/mock'; -import { EntityAnalyticsUserRiskScores } from '.'; -import { RiskSeverity } from '../../../../../common/search_strategy'; -import type { SeverityCount } from '../../../../common/components/severity/types'; -import { useUserRiskScore, useUserRiskScoreKpi } from '../../../../risk_score/containers'; - -const mockSeverityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 1, - [RiskSeverity.moderate]: 1, - [RiskSeverity.unknown]: 1, - [RiskSeverity.critical]: 1, -}; - -const mockUseQueryToggle = jest - .fn() - .mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); -jest.mock('../../../../common/containers/query_toggle', () => { - return { - useQueryToggle: () => mockUseQueryToggle(), - }; -}); - -const defaultProps = { - data: undefined, - inspect: null, - refetch: () => {}, - isModuleEnabled: true, - isLicenseValid: true, -}; - -const mockUseUserRiskScore = useUserRiskScore as jest.Mock; -const mockUseUserRiskScoreKpi = useUserRiskScoreKpi as jest.Mock; -jest.mock('../../../../risk_score/containers'); - -describe('EntityAnalyticsUserRiskScores', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockUseUserRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, loading: false }); - mockUseUserRiskScore.mockReturnValue([false, defaultProps]); - }); - - it('renders enable button when module is disable', () => { - mockUseUserRiskScore.mockReturnValue([false, { ...defaultProps, isModuleEnabled: false }]); - const { getByTestId } = render( - - - - ); - - expect(getByTestId('enable_user_risk_score')).toBeInTheDocument(); - }); - - it("doesn't render enable button when module is enable", () => { - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('enable_user_risk_score')).not.toBeInTheDocument(); - }); - - it('queries when toggleStatus is true', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); - render( - - - - ); - - expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); - }); - - it('skips query when toggleStatus is false', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); - render( - - - - ); - expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(true); - }); - - it('renders components when toggleStatus is true', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('entity_analytics_content')).toBeInTheDocument(); - }); - - it('does not render components when toggleStatus is false', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx deleted file mode 100644 index cf01417d0eab..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/index.tsx +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { useEffect, useMemo, useState } from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; -import { RiskScoresDeprecated } from '../../../../common/components/risk_score/risk_score_deprecated'; -import { SeverityFilterGroup } from '../../../../common/components/severity/severity_filter_group'; -import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../../common/components/links'; -import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; -import { HeaderSection } from '../../../../common/components/header_section'; -import type { RiskSeverity } from '../../../../../common/search_strategy'; -import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy'; -import { SecurityPageName } from '../../../../app/types'; -import * as i18n from './translations'; -import { generateSeverityFilter } from '../../../../hosts/store/helpers'; - -import { useQueryInspector } from '../../../../common/components/page/manage_query'; -import { useGlobalTime } from '../../../../common/containers/use_global_time'; -import { InspectButtonContainer } from '../../../../common/components/inspect'; -import { useQueryToggle } from '../../../../common/containers/query_toggle'; -import { usersActions } from '../../../../users/store'; -import { getUserRiskScoreColumns } from './columns'; -import { useUserRiskScore, useUserRiskScoreKpi } from '../../../../risk_score/containers'; -import { UsersTableType } from '../../../../users/store/model'; -import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users'; -import { RiskScoreDonutChart } from '../common/risk_score_donut_chart'; -import { BasicTableWithoutBorderBottom } from '../common/basic_table_without_border_bottom'; - -import { RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; -import { EntityAnalyticsUserRiskScoreDisable } from '../../../../common/components/risk_score/risk_score_disabled/user_risk_score.disabled'; -import { RiskScoreHeaderTitle } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_header_title'; -import { RiskScoresNoDataDetected } from '../../../../common/components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; -import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries'; -import { Loader } from '../../../../common/components/loader'; -import { Panel } from '../../../../common/components/panel'; -import * as commonI18n from '../common/translations'; - -const TABLE_QUERY_ID = 'userRiskDashboardTable'; -const USER_RISK_KPI_QUERY_ID = 'headerUserRiskScoreKpiQuery'; - -const EntityAnalyticsUserRiskScoresComponent = () => { - const { deleteQuery, setQuery, from, to } = useGlobalTime(); - const [updatedAt, setUpdatedAt] = useState(Date.now()); - const { toggleStatus, setToggleStatus } = useQueryToggle(TABLE_QUERY_ID); - const columns = useMemo(() => getUserRiskScoreColumns(), []); - const [selectedSeverity, setSelectedSeverity] = useState([]); - const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); - const dispatch = useDispatch(); - - const severityFilter = useMemo(() => { - const [filter] = generateSeverityFilter(selectedSeverity, RiskScoreEntity.user); - - return filter ? JSON.stringify(filter.query) : undefined; - }, [selectedSeverity]); - - const timerange = useMemo( - () => ({ - from, - to, - }), - [from, to] - ); - - const { - severityCount, - loading: isKpiLoading, - refetch: refetchKpi, - inspect: inspectKpi, - } = useUserRiskScoreKpi({ - filterQuery: severityFilter, - skip: !toggleStatus, - timerange, - }); - - const [ - isTableLoading, - { data, inspect, refetch, isLicenseValid, isDeprecated, isModuleEnabled }, - ] = useUserRiskScore({ - filterQuery: severityFilter, - skip: !toggleStatus, - pagination: { - cursorStart: 0, - querySize: 5, - }, - timerange, - }); - - useQueryInspector({ - queryId: TABLE_QUERY_ID, - loading: isTableLoading, - refetch, - setQuery, - deleteQuery, - inspect, - }); - - useQueryInspector({ - queryId: USER_RISK_KPI_QUERY_ID, - loading: isKpiLoading, - refetch: refetchKpi, - setQuery, - deleteQuery, - inspect: inspectKpi, - }); - - useEffect(() => { - setUpdatedAt(Date.now()); - }, [isTableLoading, isKpiLoading]); // Update the time when data loads - - const [goToUserRiskTab, userRiskTabUrl] = useMemo(() => { - const { onClick, href } = getSecuritySolutionLinkProps({ - deepLinkId: SecurityPageName.users, - path: getTabsOnUsersUrl(UsersTableType.risk), - onClick: () => { - dispatch( - usersActions.updateUserRiskScoreSeverityFilter({ - severitySelection: [], - }) - ); - }, - }); - return [onClick, href]; - }, [dispatch, getSecuritySolutionLinkProps]); - - const refreshPage = useRefetchQueries(); - - if (!isLicenseValid) { - return null; - } - - if (!isModuleEnabled && !isTableLoading) { - return ; - } - - if (isDeprecated && !isTableLoading) { - return ( - - ); - } - - if (isModuleEnabled && selectedSeverity.length === 0 && data && data.length === 0) { - return ; - } - - return ( - - - } - titleSize="s" - subtitle={ - - } - id={TABLE_QUERY_ID} - toggleStatus={toggleStatus} - toggleQuery={setToggleStatus} - tooltip={commonI18n.USER_RISK_TABLE_TOOLTIP} - > - {toggleStatus && ( - - - - {i18n.LEARN_MORE} - - - - - - - - {i18n.VIEW_ALL} - - - - )} - - {toggleStatus && ( - - - - - - - - - )} - {(isTableLoading || isKpiLoading) && ( - - )} - - - ); -}; - -export const EntityAnalyticsUserRiskScores = React.memo(EntityAnalyticsUserRiskScoresComponent); -EntityAnalyticsUserRiskScores.displayName = 'EntityAnalyticsUserRiskScores'; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/translations.ts deleted file mode 100644 index 058033758549..000000000000 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/user_risk_score/translations.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const USER_RISK_TOOLTIP = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.userRiskToolTip', - { - defaultMessage: - 'User risk classification is determined by User risk score. Users classified as Critical or High are indicated as risky.', - } -); - -export const USER_RISK = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.userRiskClassificationTitle', - { - defaultMessage: 'User risk classification', - } -); - -export const USER_RISK_SCORE = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.userRiskScoreTitle', - { - defaultMessage: 'User risk score', - } -); - -export const USER_NAME = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.userNameTitle', - { - defaultMessage: 'User Name', - } -); - -export const TOTAL_LABEL = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.totalLabel', - { - defaultMessage: 'Total', - } -); - -export const VIEW_ALL = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.viewAllLabel', - { - defaultMessage: 'View all', - } -); - -export const LEARN_MORE = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.learnMore', - { - defaultMessage: 'Learn more', - } -); - -export const ENABLE_VIA_DEV_TOOLS = i18n.translate( - 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.enableViaDevToolsButtonTitle', - { - defaultMessage: 'Enable via Dev Tools', - } -); diff --git a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx b/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx index 910e89f9b38c..94fcd69e962c 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx @@ -7,6 +7,8 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { EntityAnalyticsRiskScores } from '../components/entity_analytics/risk_score'; +import { RiskScoreEntity } from '../../../common/search_strategy'; import { ENTITY_ANALYTICS } from '../../app/translations'; import { Paywall } from '../../common/components/paywall'; import { useMlCapabilities } from '../../common/components/ml/hooks/use_ml_capabilities'; @@ -18,9 +20,7 @@ import { HeaderPage } from '../../common/components/header_page'; import { LandingPageComponent } from '../../common/components/landing_page'; import * as i18n from './translations'; -import { EntityAnalyticsHostRiskScores } from '../components/entity_analytics/host_risk_score'; import { EntityAnalyticsHeader } from '../components/entity_analytics/header'; -import { EntityAnalyticsUserRiskScores } from '../components/entity_analytics/user_risk_score'; import { EntityAnalyticsAnomalies } from '../components/entity_analytics/anomalies'; import { SiemSearchBar } from '../../common/components/search_bar'; import { InputsModelId } from '../../common/store/inputs/constants'; @@ -55,11 +55,11 @@ const EntityAnalyticsComponent = () => { - + - + diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx index 9c5671640f87..00b16daa4610 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx +++ b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx @@ -11,9 +11,10 @@ import { useRiskScoreFeatureStatus } from '../feature_status'; import { createFilter } from '../../../common/containers/helpers'; import type { RiskScoreSortField, StrategyResponseType } from '../../../../common/search_strategy'; import { - getHostRiskIndex, - getUserRiskIndex, RiskQueries, + getUserRiskIndex, + RiskScoreEntity, + getHostRiskIndex, } from '../../../../common/search_strategy'; import type { ESQuery } from '../../../../common/typed_json'; @@ -51,8 +52,7 @@ export interface UseRiskScoreParams { } interface UseRiskScore extends UseRiskScoreParams { - defaultIndex: string | undefined; - factoryQueryType: T; + riskEntity: T; } export const initialResult: Omit< @@ -63,64 +63,48 @@ export const initialResult: Omit< data: undefined, }; -export const useHostRiskScore = (params?: UseRiskScoreParams) => { - const { - timerange, - onlyLatest = true, - filterQuery, - sort, - skip = false, - pagination, - } = params ?? {}; - const spaceId = useSpaceId(); - const defaultIndex = spaceId ? getHostRiskIndex(spaceId, onlyLatest) : undefined; - +// use this function instead of directly using useRiskScore +// typescript is happy with the type specific hooks +export const useHostRiskScore = ( + params?: UseRiskScoreParams +): [boolean, RiskScoreState] => { return useRiskScore({ - timerange, - onlyLatest, - filterQuery, - sort, - skip, - pagination, - defaultIndex, - factoryQueryType: RiskQueries.hostsRiskScore, + ...params, + riskEntity: RiskScoreEntity.host, }); }; -export const useUserRiskScore = (params?: UseRiskScoreParams) => { - const { - timerange, - onlyLatest = true, - filterQuery, - sort, - skip = false, - pagination, - } = params ?? {}; - const spaceId = useSpaceId(); - const defaultIndex = spaceId ? getUserRiskIndex(spaceId, onlyLatest) : undefined; - - return useRiskScore({ - timerange, - onlyLatest, - filterQuery, - sort, - skip, - pagination, - defaultIndex, - factoryQueryType: RiskQueries.usersRiskScore, +// use this function instead of directly using useRiskScore +// typescript is happy with the type specific hooks +export const useUserRiskScore = ( + params?: UseRiskScoreParams +): [boolean, RiskScoreState] => + useRiskScore({ + ...params, + riskEntity: RiskScoreEntity.user, }); -}; -const useRiskScore = ({ +const useRiskScore = ({ timerange, - onlyLatest, + onlyLatest = true, filterQuery, sort, skip = false, pagination, - defaultIndex, - factoryQueryType, -}: UseRiskScore): [boolean, RiskScoreState] => { + riskEntity, +}: UseRiskScore): [ + boolean, + RiskScoreState +] => { + const spaceId = useSpaceId(); + const defaultIndex = spaceId + ? riskEntity === RiskScoreEntity.host + ? getHostRiskIndex(spaceId, onlyLatest) + : getUserRiskIndex(spaceId, onlyLatest) + : undefined; + const factoryQueryType = + riskEntity === RiskScoreEntity.host ? RiskQueries.hostsRiskScore : RiskQueries.usersRiskScore; + const { querySize, cursorStart } = pagination || {}; const { addError } = useAppToasts(); @@ -131,7 +115,7 @@ const useRiskScore = { test('does not search if license is not valid, and initial isDeprecated state is false', () => { mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: false }); const { result } = renderHook( - () => useRiskScoreFeatureStatus(RiskQueries.hostsRiskScore, 'the_right_one'), + () => useRiskScoreFeatureStatus(RiskScoreEntity.host, 'the_right_one'), { wrapper: TestProviders, } @@ -60,7 +61,7 @@ describe(`risk score feature status`, () => { test('runs search if feature is enabled, and initial isDeprecated state is true', () => { const { result } = renderHook( - () => useRiskScoreFeatureStatus(RiskQueries.hostsRiskScore, 'the_right_one'), + () => useRiskScoreFeatureStatus(RiskScoreEntity.host, 'the_right_one'), { wrapper: TestProviders, } @@ -76,7 +77,7 @@ describe(`risk score feature status`, () => { test('updates state after search returns isDeprecated = false', () => { const { result, rerender } = renderHook( - () => useRiskScoreFeatureStatus(RiskQueries.hostsRiskScore, 'the_right_one'), + () => useRiskScoreFeatureStatus(RiskScoreEntity.host, 'the_right_one'), { wrapper: TestProviders, } diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/feature_status/index.ts b/x-pack/plugins/security_solution/public/risk_score/containers/feature_status/index.ts index 0099ed0df3f0..4cb6507c559f 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/feature_status/index.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/feature_status/index.ts @@ -8,7 +8,7 @@ import { useCallback, useEffect, useMemo } from 'react'; import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities'; import { REQUEST_NAMES, useFetch } from '../../../common/hooks/use_fetch'; -import { RiskQueries, RiskScoreEntity } from '../../../../common/search_strategy'; +import type { RiskScoreEntity } from '../../../../common/search_strategy'; import { getRiskScoreIndexStatus } from './api'; interface RiskScoresFeatureStatus { @@ -24,15 +24,10 @@ interface RiskScoresFeatureStatus { } export const useRiskScoreFeatureStatus = ( - factoryQueryType: RiskQueries.hostsRiskScore | RiskQueries.usersRiskScore, + riskEntity: RiskScoreEntity.host | RiskScoreEntity.user, defaultIndex?: string ): RiskScoresFeatureStatus => { const { isPlatinumOrTrialLicense, capabilitiesFetched } = useMlCapabilities(); - const entity = useMemo( - () => - factoryQueryType === RiskQueries.hostsRiskScore ? RiskScoreEntity.host : RiskScoreEntity.user, - [factoryQueryType] - ); const { fetch, data, isLoading, error } = useFetch( REQUEST_NAMES.GET_RISK_SCORE_DEPRECATED, @@ -52,10 +47,10 @@ export const useRiskScoreFeatureStatus = ( const searchIndexStatus = useCallback( (indexName: string) => { fetch({ - query: { indexName, entity }, + query: { indexName, entity: riskEntity }, }); }, - [entity, fetch] + [riskEntity, fetch] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx index 831ca2bdc76f..06edd9607386 100644 --- a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx @@ -15,7 +15,7 @@ import { TotalUsersKpi } from './total_users'; import { CallOutSwitcher } from '../../../common/components/callouts'; import * as i18n from './translations'; import { RiskScoreDocLink } from '../../../common/components/risk_score/risk_score_onboarding/risk_score_doc_link'; -import { getUserRiskIndex, RiskQueries, RiskScoreEntity } from '../../../../common/search_strategy'; +import { getUserRiskIndex, RiskScoreEntity } from '../../../../common/search_strategy'; import { useSpaceId } from '../../../common/hooks/use_space_id'; import { useRiskScoreFeatureStatus } from '../../../risk_score/containers/feature_status'; @@ -24,7 +24,7 @@ export const UsersKpiComponent = React.memo( const spaceId = useSpaceId(); const defaultIndex = spaceId ? getUserRiskIndex(spaceId) : undefined; const { isEnabled, isLicenseValid, isLoading } = useRiskScoreFeatureStatus( - RiskQueries.usersRiskScore, + RiskScoreEntity.user, defaultIndex ); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 4ab7756b5fb4..151159fa0980 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25859,7 +25859,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.alertPrevalenceTooltip": "Nombre total d'alertes de même valeur dans la plage temporelle actuellement sélectionnée. Cette valeur n'est pas affectée par d'autres filtres.", "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "Champ", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "Valeur", - "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "Données de risque de l’hôte", "xpack.securitySolution.alertDetails.overview.insights": "Informations exploitables", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "Alertes connexes par processus ancêtre", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "Impossible de récupérer les alertes.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c01a643d5e95..5465a9a7be7f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25834,7 +25834,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.alertPrevalenceTooltip": "現在選択した時間範囲内で同じ値のアラートの合計件数。この値は追加のフィルターによる影響は受けません。", "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "フィールド", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "値", - "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "ホストリスクデータ", "xpack.securitySolution.alertDetails.overview.insights": "インサイト", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "上位プロセス別関連アラート", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "アラートを取得できませんでした。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 75eab045500e..e2de967852a8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25868,7 +25868,6 @@ "xpack.securitySolution.alertDetails.overview.highlightedFields.alertPrevalenceTooltip": "在当前选定的时间范围内具有相同值的告警的总计数。此值不受其他筛选影响。", "xpack.securitySolution.alertDetails.overview.highlightedFields.field": "字段", "xpack.securitySolution.alertDetails.overview.highlightedFields.value": "值", - "xpack.securitySolution.alertDetails.overview.hostRiskDataTitle": "主机风险数据", "xpack.securitySolution.alertDetails.overview.insights": "洞见", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry": "按进程体系列出相关告警", "xpack.securitySolution.alertDetails.overview.insights.related_alerts_by_process_ancestry_error": "无法获取告警。", From 36abd986dd54cf2e5432fee0d7aaa6d37890c84a Mon Sep 17 00:00:00 2001 From: nastasha-solomon <79124755+nastasha-solomon@users.noreply.github.com> Date: Fri, 30 Sep 2022 14:52:36 -0400 Subject: [PATCH 173/185] [BUG] Osquery doc updates (#139583) Co-authored-by: Joe Peeples --- .../images/live-query-check-results.png | Bin 142848 -> 355410 bytes docs/osquery/osquery.asciidoc | 7 +++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/osquery/images/live-query-check-results.png b/docs/osquery/images/live-query-check-results.png index cd1362e7e977dc6fd8617b08522894fbe8dae480..6b84a3bf9f7ca5a6d9e8e521989035c7492b2a5f 100644 GIT binary patch literal 355410 zcmeFZby!qu+dfQ#BB2OKmnb11CEeZ9(xAXdcXtR#_s}RQt#pGT9V0m~baxM3zlD46 z=Y1ca{e17||C{5OnYCuEn{~%^UvXaNHSb?4N@3k4zKei>fF&a>u7ZGYR}=vOH4+02 z_=a=600sD>(o9V3rHq&uK|Yt!pzq1Lg*+Tu)10iDfzt< z+)W_QWgvX?pZ>hym;BN zk5uOvSe7LZ{)m+|#riFab%$a;N4djL@=X>sH=PBd@P|EDw)+SV@kSthD`7wQ9}2~9 zKSuh3Vgl#d>*?xx_#u1@mEdjo!{wOE;v9;Dkn2QtndG=mJbE`vmTb{IJdn(=Ti7+E zbH9a_Mvqm$?Vh@9=rcN`s<#F|0xJ7(Y2{)~10yP4Jtr3Gp%4AAX14;ed(ffPdWfl9 zs*rh~cc6O#p_DBujf8XH+q0W4yF?R7It$uf?k&5O?k5`zLs<#e52-Yhg`UzfIHy&# zv$A*aFfy?jQwL?kdmrBKxqcTbJo~;Lv8M@SeD&FYRkKbb;{kro6rW9&&&NQ47W{aJMUKT7jXMg%!7tPdd;R{qB4de{-62mSmDtm88-qBF~g}$AM({ z-Fl~Ir^=W5R~E&5iD40Kp`S57pN+rX^0ve}W;PR1-aQQLKm<`F5z$vN zbWcdB(`crs;SYjB%*9o+_*lc~D4D6PDR*dI;MzR!2oZbi6wA{c_fSHXwuCYvx=3Po!V{r0ghkB)AW9-WSm4u^iXK&_ByG&n^# zK^3aX_zC<;sUTDBEkiYfF1}xneF9s8c7hIrjoSOdyh6*-xt+VuHK>Px5>8|h<%93|Dw4VE6IiRk!YBk zL(mG}iej6sBselVb+`_$=1z5m(UdS#R`78>hbX)5Wrh4#qgI1tKZc>0BXJv6(1x57?aYt6*_nkc z5;tkynTY+Zy~38C+vgg;%H=(Y>z4;5K2J@$3aSqh3A)=R5L6N*Cb=RxD9IR67?JTP zE?ldlyF(|OB;rEho_t>FGu8qQH)B~2ljOW4Yj&#_WE|{VVVpWv8ldBEy1i)@8E^E> zW{gCt_NtR?l&$SLAAq{cMIsim6kpgV&|hKB?caZGvP~ zeABYf(wzQm-E7@EJy7fA>|zIQpm4eAZR?=vUySYyGg}!B96naqJmxr9a@*P8g1>gB zb-!8|UAJ26KV&}fUU{=cvp2Cj+*@s&Uyf5TdkQ=2@wD(n=h;ciO~dpKG5BEMXHe7< zr;h!38P zFsKp0o|9#e^Lrh_BZ>0ZAG2?K6Mn>H=`bE=d$@NtpAT9v4T3yNJ>&l-_*zEX;W3lF z{?f;;DCK5lk}Ps59dX7ik~eB^Ogq)S-|IMUCun!>P>9eWcA?RKs!wW=>8lhXGoJbU zd9Tmdn3h0AI%pNr%+={R@Qnw$veBoI>EWEQ!9UZ8_kR5Q_;<1Q25v@;_A|l{xXh$f zgfiP^JA7#O?pxl6-w}8x@omN!vXr$%NUbzzs%#MIB5T)U!78Q8;GIewU(e_Coax!N zFwEB7(%lJ-iiUMDUR*5piCNDGL^o-Ixhio*j&HRAGRxayKtMg2iE7f{T?if?oc|>=UbfSz=i>p!4&1(}4jMeaB;f3srnaQQ5 zb;B5UWkuzamGxAMn&&kSEzdqX&5T*JE4DY(#8lH)J0%7tj@WQbG}~Q3hvJ7COqxwb z`5aw}Cs=D$KOc@UXX>dp961y2Tpxs;g3&#YPDT<_69{+PxBF)lFv^Ii$;?@i3NbipbYDri zs8WS$K{u^yr#@NPSr?r3pVg#jfX85|rCghnQ!rv!epP0}ve)K9^Njq<>g?^}6c-I!j?uF=O#R6*Da-;R)g!A4g9SONKesOg;V99LJ zK3_cdnCY3pZqd2Wzqsx&@BSbQ*f2eb9fP z({>WQOV&oz)~1pX&c%C|^3%8OZiGfYz`rC%Am$Z|HjU^X$wrFsLJxm%s>J`oB`OpX zLH!dEQ3N+30(WSk7BfPjAVPS!gt8!lv!mL^=_Ma;QB;WFdC7A*?2s&+s4;;a)SKup zJlDa^H)da@HJ49;7RU~`v6d#UWK0zl5Ey`C3; zSQ3#A0r}_mNC*hQW(a`i`|}<};Pdt`8hG8d`QsDm1JYl2+!g(R{MRvR87v%CH(;`CWr7)S)X46*lk?^NFsHz&Yk_tRUWoD`Pgn2 z7r7U1?08Dz+9X1cQE-J35P##1atUQ2SU1e)fa(4ahlcnzfT~^5t z>!AL2zk| zu-|N`2=p_Zc^B>7 zqhbU)nnz3h)^-A(^?(wWEk}_sLgM!tBIW~a6|oL}a~tBSAfbo`jjU&V`OSuN4=Hhn z3Pi|%b2Ev`0(&@`SC50>Hyfq_{rqS1{VIe1Y`$M-7?9sg(Z{k=*2$MXH1 z8}^Uo`+Jl4yOz%n7mwRzr%^ujg`V@y6#i?b{5s$t3mc+5Kr?0k(vrYw0qu^ZmD?tR zmT79*FLhZb;*fIF#XV8_0_I`gUuciJM>Hgt_)IHD`1&%I9fZC5`VS*d#bCSGfwg$^ zuEqE)6u%s_E6C%%^vQT2R?B|1hkm&i95<9J8+uJ4Ec&Y_;rHX=T(iyF3@+^mVMi2Z z{YE;Wdlc*1oIcl{D*4V#Q2x7rja>NC-C~^<5dtP{OD-XU=Ej4!=`d%W#=dXw!c4A$ zu#OiN+Cyb>W$~A`>Ks(c)p!OCr4GAK5QFIZG*S~-3_5w0@pSvq zdQSonkOm1cCWNK0YJupEiM~5H(Cq~+?{{v9t3{an z?2O2>=bI-JLroqmXD$%(*^;fBcMqA>r0bnFsS_v)mKv}f{=*-n{DfM&OEQul+(Cu= z($DRX&{benCYkM>rEZJ%gD3YUq;$NA$ON3_nRROoq5W}uHR(@*zWscfoz&b=u3YIR zS<_g7R(6nGNgU<3fXlfy@^Y&S=Z1mkugLwEmu{F_l+x+@!;ltS3^PVl&6Tym>{5rD zvD9rnzi7=e{RCum99V=R_(e&MVmgdDP6WYk4|V7K;}=2}ed-beYet*KqsTRqpr1jP z-;evXZijiE<|sVX=d-=@=pxrH+~@B;XdSK^|Cpot=-^xV@IPFpEZSDD*|%T5JU|km zd2Y90Tvq*dS2B!@=AD~*2o3v;&kZuY!N<@mJSpt3w!hqL9KTo`aCA5k`?$lAHmJQP z!FfW{v@;P*Ht9sEH2;8p;H_b|6+uA&;D zbwx!V+rtRsBrsQcdY0vk`_|^mosaG&^MMk1o>f;YH0(Mh2B72cfMWi`&j+?Wu*pKw zpQnWRW+GL1y9mv>l;)*tKd%R?2L2U=1eOb5xjX6gXRFCX3lh{yRCtD{s`VIuj$|Q3_#3x9sWNZ)_xE_c0w)h+Y=W^8flVd{dXt36 z=mod4aa&2w55IZ1BTF3SM-L1L7mjp(wk?m(5D!+SX^wj=6Uz;fi~07>_}nILsXIS2 zLA^*LwYSpZ?8~BeY|V_N4fM+BRQk+f!oDJHWL)w_>B8Mh8Gfe_|a^+ax&eqIK6GO=7rvUsbe*RAHw>ERq6S- zLxcQ9P~EU{Xip*&&B;T0(wz+pXbD&%jG%X=U&nZ{qd)es*@NzM#{9bGW9&Fyrwv=E z2_ll6y5Yj(6XfgsOmhaIYmOPgsXB+%?!_n}bFi05B!v(aZ{S>3yIkLe!#S5;y_(D- z`Q%|%ulvc>h<=I0p{dH3*KQ4#CXr-(%tBE&?Tw&X7l@!t+>5GsCRJ_OglF`*vI1L& zyTfi#Wf~D1L$!8dTIWzUtwz^^K7-FMOZA%=+IJWk6jC_YYD~7}@(u2t%D%9fVCYRO zvmHnTPd1t+9uoOs?KVvgR)s@w3DHsi*iWL0a{wL`b9r{yU8>j6U%h$M+0&w#48a$` zk>qak!~X2H41E5%65_S#PHra1GlYiJC{}O-whn`?P1nyn3x>wYwz*Bg#e$~6f?<0-w7*hB0`KCzl=-SD`>KP`2q>#X#rvJiI_$Q+g!oM&npE{EbX znrw{B_VJ!8wV@qt@e}>3Aa~FZeReO*h2v*fK9lonzc| ziV|A4py=vLuJA+?HD9G4famNFB~}EKVG=Uem)ccUJc0;!t?4eVg}olIi07Jp%Tl0{ zTb`QL=B5K>(8Y4m6-SrnJLhO;g{yTYG6#&|pk^;;ixbylOKBO*BK$3ua=r z{W5Bj_xOpVZ8kneo!PMIVJ?xuEQpYV1to%w`aS&E2<3VH(drRF}IS6{-}Ed)xVBrAjP-B|gW zVpW4Xgi;z}wLZxU;jo|wU2k3uI@Beu_N6lpP)Zj?9y?EbHbO3XOI-Yl$oX(I!_3(9 zh83#Cfkd_0(DU%uSTJN0>3Zs9)B}?#(*l3}3ek(MKtwKM{qhWRC~Fxrru4?S^TfCn zjI39Lb8VUgJXn)9`$x_?JK$sLLnh+yhg2X9gZk{rQfHt;=)LEyQ?l&gbH1wiidqs- zwVP6%M?TOo%+Atxk|M~|e9gn%4%J1CUY*!(d2Vu0eu%&I&AKU5U#khkM8j<`i&kGG zdWz zVWgP0NGnulY`G_l>a3v={+bcWMurvv=uqmtUKTvvVC{Tog^H<>N3yVFz~S0{20JUI za6TbsEBB}=Jy;x9KCmkz&pPQT_OLmYjSN42P=weuz?(kf(Rn#}>9F%G`%Q$-$&F(b zbh2gn)nxjphi{6$Yjf^BBK{wZo%t6oPp&LK$40))KDmEX(Ut6&aeeP{1JmP0Ce;hn zKlPxfzOYo_FvwqHh>%&M#2%ewtoWB26}73PNYr2w)$1F>PuJu}3pHZ2*zonEM(~%J$Pp`^`PzUbx?y z#Hgv*LYxFdbDB-roso4ttn{9T{m-EraZkd*o@WOshX+dpXU^k>+29b3W3f2EFJlWJ ztpZCK6dcKwgjc7)uG4EX1RF^#8R-PMs+~- z{Ng*=$xvopG;f9nhYV!M!5fZ~2HR+@V3kk&yl)1QX@VOk3&IrGT=!lM2Fs;5dVvgc zjy1SUddgxMOk^G0wzWBF0kYM>#42)MxnS+^%)Fx-W)sC1w?hJl=Id;h=P%43y+{!| zhp+b;L4mM}QZr({q5{o3 zlOVkDiEgd!mmoF470T5z&m)g z14r*L3H40=h}yr>+U_sVY6hs}*|x}RYp>BgR?3)Cez3T3q@i@L6jk}zTHBpf!stY% z)Jc)(0fna11x#mU3SY|?{se7uFua_ydD{n%rRVCqi)<2Yrkyu~)=8sMo3J*Zm^Orc zzeYKfikRY#vo6P$C&*WhAmd^$sddVAVN!1s@B&FmiRgGA@k1_AkkyR(|S%~wH0{Oj6&NJw^S zhA^99J*lzJUrR2u-`yqP@X)JbSewm0G%-vs6L_G-f@7ftWzpCY?1vQ?enrw3sD@!i zgjZm}_~O_Br|v|zmq24IjajF9O1=6n>|&8!NyrSp-)y4Xq)(r|B-!1Dr4T0_e11C-3YuUX{DIT_5B5-3VCA-nEy=Uylh1;SC2Iq2+o7+m3m z`WhK-a*d z^iaM)H9rw20znRvGkZo+qN-!smuAAUF_R5mi^49`%d<2qcnjCo%#}6KY>n4T26Tkl zTz;mv9hp{%HmB~gWCXGFuHe;{+xgriPyA(vLh80j%HWKK7p)f^zk46IL74H$j?dN^ zan7p(d#46c{godd+XM;mpS^6|lmKD*tXv6mS7a0sSxPtxF)*UW~~3^93warc8;KS$GM@H~A!!y{CDJ>-phM7CeZ`Q@-|kw#fe zPn24$5UBuz_o)cs`l;g6ik{bR##iLP%TPKRv85>Oz`F{JqXs|l3DS{+PgDSuhfKhOPw-y zlqAqWr@?vKnZRpBDdXOz#5V_{tnWKJ0|H<|Cik+nXq_>^JV=sBb9c5))27ZV4fmDx z{6_0)?cP5(z`w$5N?a)Ft*^1RcON>xI*`x{Y#_J8l`rKh@YDBrEAS6ebE zjPUbMsaumR24f^*mV9y86-7eldkS%@pBo4M5LVu)Ml^C>)kA_BNzK8t3>N$wb9nsH zy}*%=Rq6EE_g~I2n)J?$e&x)Jj-ft;wI=dml-> z`KQXBLc$^$BLspJ9bSjA-7LT83e&pacJG7k4rShGVco@ecK0ghyq0V2C#VOjE=NB~ z+B|?c9Bd9Mb^EL- znb({Alc!Bf3+w?UkS*eB+T;&%)re zyYW$auhXk^eToxSP>FVxcI2ZbxoaL9zlzY2l8dAwdBk53L%)Wf);~k6H-q1;2gE|b zJDaTXSjmV$04n5h9G)Y$N}keJmjTk>y(6D~StOtSV!Bain`fk2{bm#j>rG|{<(P!6 zj6KW4_%4G8!g)W2Xg$-|-gBO2PPu{;^|`6s)kR5!hdLGeT~(rvEn*B2kcy3`=JE8 zhY<&xA2;)K2N+o}Ev>ibIXFL&Yol3Q9(_N zAAPHt7E}O07H8m-PS-+|jdt$-UJ9aK!W-3lDRdM4kUkduEyNPdZw|k96PfC$WR9hU zc!^g`iC=Lp1Tzrn7GyI$VtW3$TD9 zNlW#);UY#f*-*l(I`6A`p2xf8%Zy8tHa^ci-c@Rl^6flDqGGz2XQv|UbK^t4OHMF3 zi=1wXg4Z%Q?^JQ_b%O6ya(psdl=Fo(tr|4+k+eP(a9|i(NO*m6$49I2xXrH;YW~Prh=sAokGy%>hRhq_Ak~KRFY*4i#jdtw7k^39o z7gj+{Hw^0JtcSe1)%X}ZCy=x9{(Y}P=WSx=D`68PJ6#w7){_wm1sF-ru_@2MDf!CQ zN7XVuB`{)|J+|DD-lr5}4+Z`o3-qP}M!%tg<(>(PO10G3y7U&qU7 z*kMk>6Zte_xUkzG18f+pqbgh_S; zHXK3DL+(|aamBt}v%Zqmba9+45D`vvk$vyf{=*iKU;!LEL-;2Z;S??pZPWfV271+? zBmM%$w`DHPJM6D6j6#J!VLszR@=X`mT@xs~({jCZ2wIC4DQz3|cs({-iiXO4<186Y z%5-zx(5Y@ffn*USIG=m4+zShX+dwA5Vyfnry5m-wB*{eWx?^+g;p40pgI38I0xknp zdV*rQq<P{W-#Ha?L-BPRQjaby8+5;jy0IJNZI$e5 zoj1t`n^h0dNJblfP?e9A^O(6Ac0IlTBGaW!k=ZVT-o(`YVNi3FfVPfg#HGGDv~b=0 z;QYvlrQW0`zMIpag)4*GZirJUcnn)@2tf)|Z?_oqOuupDUdkv4;(jk>C`aZEn&a{M zw4D2~ElzUD*Yw9fvjtgFUFm!-6EQHr$HQl`fmKQXp35?C=Q(O!6ssr09~%7>m6+qx z(_4tx`%qeE>O4VH0vX{*R67_l_OZ+h>*Mf8FFsA&kEHNDEKlLQyRO}$(f^tJ zWg?3KeHmZ%Rn5My?deI|WADq)-k9*(V&`=<$M2!W-LZAm5{Q1akDpJxsg-J4RnK!Q zu*Dg4gD82}=vu&~;d22vNhfQCY6X_fftDr3Z3wrnQ+Mjq8o*T9D=pP^`a0T~R)e9q{XCSUBbm;%A2nNrv> z*-UICSzEK1A_s3jkvR&TBp2<;qi^m#@w4HZUkM~9RZ<~|M-z@0Tr+GQWtGPh$%hw+v6h=bIdFjzaKpnH$qsDNJA(kdaLu#;JoMD+5**y48LI!?8i@ z&093^gph?f7@Hzdufe%tY}B`Z%~5Z6D#UwY-xE(By!m-ntK8U5gZ5XBpKuypNVk`P z7pvV;$ZoFkV!kC<(CZ@X+Ewt!(A{&rhO5monA6c$=vqu$u1I#?G5_|ovJd>72LwG4 zKb%~pI3|SO1AmaR&tum^m_fbxEq@m;9dBP6`)JWgV1+cfA2+S-*@4L*diwyN^}n}0 z*+AZnazCopOy+k=A;KtNclVhZUc-1(;l-kOLvV{2mXc`Y>DHAW9P5yMcc0fXDEH2h z#bT%AX|@wm@pxhX#48W}eKCp`)Xwdr6hmEV<+ynrvj&M%dJzM;I62x#4gs6!ev5Wemg2*2@@fyYwkYn-AG@DxEaF^WU zT2GbU4-eCXQfZR|@&JiOV(4}5DI+8$Fx6}jt}*=^*UXS( z{24+1)qJH)Mf0)Z9Uh$&j@Pd&Sst4HLjRa@LW-F_86}qqx_RE$+g=fj*R=cGvdeIA z_j({_cGu_B`;$Xr^2t5_k*j-GrXTc_C`7L~tSj}#!b`Jh9=V~3jFnu}apwu2SBxL&F zF_x1ps`;N+D-=4}2{DXk1W=_(kz<#Z~Y1U6Rrm2rjhX;~%jKAitk%Jkj?O z;3m;YIk(P5!DWah9zS2|KB=gV<|mDk(EZ(Q8CITBL<9KA#xpA*cLKo9Fq*Sqy;&DB zVE}1q8gtUPMNuniwY;v*u~sf$lvNU74LCm0JD!9m0}TVvy_S!> z#_Gs{y&UW7@>n{ATyH`A{Z`^r*@ydJ#Vh+lt^&uTxPmyJ1fmv&>R`^#T%u?{BY?*! z9KLo#-}l*Ee}GFV#f$dOlXthw^^oy|ZWiP$zE~9Auz21~<3c40nn$NM-mMzLKT-7U zbm%dNUIyEP##>4EOFhuX>^59Hu!4p>keZuUEdGu*gUAjAnbeg4`D!u7uUlmHLE%4vU6))5{K8VxNsi8wbm`ipsg|nGB?#n zsM`R_aHdU~?dC3Y2;CQQ7Z@MQ@O2E4RPYS?9+H&s2mFm;?l;sCrsgg%mt3e>w8F#K zvz(;AsP^gQfJJ6*Z@hi~Fjb{Mi_qEKNXL?gZK94*j2_39Y||lx<^UjG_QE2rGMlt% zC-sGizovYQx6GbK|^5x zEk5TP`79!ke6vbn8kII^hP+9M8;Z0aE?MLJ;u1*XiKUtR$+-=TZOxSfF9Gm_{lfP< z-3ff)D>Szga*FOBMzfQ_Yzx6QP4(tAgv3A;HPB_Y__V%F)1bjw0h^rHxNTJdLVj^im59YuDPkElOGAzs z@>w|CkCBK56N`_^3|g3hJnNymA^uD~FUQ`;w&3Npm^YHG#>CyJTxEl8y8e5G>=cM7 zuj!t*Kay=wn1pVZtTk~o<*zUjLHd@uVrV*M*Y3ZGPj-!E6+`Pz<8dgyiU6Q5Wo&?* z1Y?Z$ezItYw4v^{u?{i;;92K5 zsff#r8h1-9wqM<&TZQKm*9ypazlTW}0n`2MdC)0N+vAuw;;dfMm)~x*d$%SWED6YX zipAdtZ?XN7kM;Y33$U~~2JlM6e{!oRb^V&J1mn&Q36pG&*P^*K3W2EL-i>#Xy76tQ z`4{y{>D#JDy9Z=4_ZHgaw%&{(baqk{(VXBjF>G>KMia67fB_pc*;@scAd}=X`WAT1 z|7-t0QURR%w`otc-~asY!nH`Wa$v3o6Bi9>fZy~i=_D9%%G!BS|Du`vdk0)1S^@4u z4q(Xbcc$E0BHaMJaGM(U9?ktF&=j`4k>TeaIh?L;K64{y3cyL0r@VUshSx6>C*!IPYFd zmFhMzOjMY}QA^yFnHPPR6TKhz+{mAAT+}(@-&X}!6%j&Cbtf>qfNt}5%R7p1r-+Ti zxp=UG=Fd6$i)!_Monll1oP9q66OX?g&%ZC)|Gov+0wBY85OebV2ea=NyyurwKVblt zB6_P_9sKXZ{~ufNUyi;!0pzV%`JVy)s)GMn!N1XNx5D+0i2f=ne<}rF4?eyJN27tw zbZnzjYxlAJUrOQ^WrAVkcX8Jw>S%13#<)1HLhN^&#|sx9W4Jw~7C=UAcm6MZ{6%g2 z>y+{oW$ixO>A)SP-&8z^LO?MT952-Te`~S-qHF&5Ex4Y5qC=bI`6%(fzy1HwaPGDm z%3D+!>tB8Ul>`0v7yOlg@}xPwrz;4

    fda*cUz4p^zl>BZ<>y;PXW`hmyV|$_?r#A zZ_5#%J{HZoH%55R*R~N;% z_kXkD>1{z`=1VoP|JtR$z2G;82l$@DQ;x®t|Bo%P^Tf|* zfHaO%>ZB{9qH}3#w3o_djUJCShQENl2bsu0PX7&Y;rivfUi*VSR34xGa=<~SS;D3< z`gR&it@S_#%v+Zuo$+`HX2Y3uXeje1k@KezxBB?GN{)^VY;f7kkS;un`;|xXKax&4 zj=~|m^ewl@ez}LU&YK4A?{v8@k1N#tSTXHo>{Eq@Lx=*`q>N5G)_y|AmhsEMF6~5t zLs|!N5mP7cruIpB-L)e#c`59-vCF;^4dq1_Cl3YIaKBx3Aoo8W`SJW;TF;LXFNBa~ zrgz}UAkF_*S+MX+Ox=1!_OJ6TmSNN#74Q>>AO&_x`&Z#`|G$nxBXU_5JY{pB7SX zTVcmSnL~}941tE^{ur9zxBB@mZ?wO>mZ%j>lmVdn9ZBLyBELtsR)R)QL-OZ5#fCQI z{PkM^|1)v_ItpR(bC6_-e!B3|#zfg}xD`Uv36XWr4z7r&;-9imi%hP!GY}4LtM*4* zgbi-k;mfDe8nzi{@;GQjk_nly%xwc%8j^v0+yCN)>V* ze_yzj2~c;F0l!3;e`!&m<^mVs!A84S;N~1UeiHeHvcFC=^j=iZ7?x%=u1?ucRDRY|XaUkYXbrv;7GnQ}kv9x!j4|#9~LHCy2 zLcAGY;oTbZIcJQwtr^$caif#jicZOBCp-aXX@0(A=k-%sopntw2R8TJ$ia!D-#4g+ zc?ChZIRl4|M^lkirxzf z7i}-J+>hgLQxAL(yd=;kN2s`XTeap3oYtW@+FwLU#&hMdNJUn?Q@%MmJT-sB4*KHbF-I zI5ZJ=G*N(wrZTe)|9ItQToT7G!S*xv{-5jYOM%2|S%i(Ha2M|-Ca26-+=)xsz}nWj zB0D~W-KkMIL>@7zNOCUyo)-Lj-UfrMXrlUnI;PydHx{9b`QwHnEjTHCUN}8Csmj0V zr^-R1{UG8F;Dyy(`Cr}UQP<4Mg6sP#PQ>&kv>|tohd+E32|#;~rlu|8cdRH%_l8VK zK4pvO+4_{J>lo_<#U?c_A%V+L4oDFXWC;~jXs>Ti1 z0gRD-cWbEDSZlw2(|LDd$>jFL+2Lx{;g|G5%ApL3BPY~&v(}83^&j(XgS<7@)!%B2_CK|i5NjxALgZw5P3Ra1M|O*t3YuyB{Kb+5IWFS}SU7WiFQ z;Xv?Pzej?=B6`%x2h(N;VT+zKeHV^V319;i-Ak$-KU| z;!*m63oC8JSAKicu0O@e8MFTARGgVeB>g^CGKo$n-!)jKI(@p8FM3dTusIVun#W*EaA#Z92)IzD2Vqb$tvBymJEpX0|H5^K4V; z`GDw$n$;<1-2t1KM)i(J5{hLp-FnBmrU9VxE1uB6Q!$i8pbn_LY8;5BYHE^aXS~t3 z0w0mK96Ec@Yc_`i)QpazQoWdaf8#X!D)}*XNIy}Q0T97>q1DTID8q7(W^}M@JiO53 z98OQfZXO8rG+?)19$<3XU{O*a^+k9zz_G~XMcRo zE|%!FR~DZQC#$X6iLG@`vX@`V5`X{nH8h1k;L();_BOt1jS;G{_NJC!6tHI0^7r=$ zFwTUf$+E^a%#;Hn1TW_V=nYE4y>ig8N#nh*S{@b!*K^HoJY&ySdLArT&3FYqvllji zXw4_K5FMmnKKBf-YjpjRCAc|VYp3cC-?Bm%F-OQbt~`;>J$L|AjX0!NfFM0!123Am ze0i?krVGy}KH5zd0B>wPd z^n;|{?vJl+ysWC5VEyQuDsvG`96M@`GdjgS3qd}mC<&% zmax)kvIG*j(Eg3$?3rs6HC(5|4dcq8>;6w%0Yy?c;9<$bE=p;ORNm3;jlJ;{N1qxC zp%rYT%P4%5b)xT&K25u#iEVgi4NV$XVcvO;&xN z-kALH*2Ger{&dc0SXmFC1pDX8fih{?bQq`ZQKcd3Q+KAlq>o~HIwH~aYd$v?>&i66 zwLqm)L$FXfyZO?~GGBTu*Dd&r&H+To!+5(MYb6vQ$i@K#S&Ye$#aUsBvNyw}mb2al z#Dk-XuH>Fp}cuA>0v-5w$uTX>r_#pK~t|6AMqgmTmVt&_Uyu{+-#SHdTb zP3>0)XoWmasvgTMU>EnqJq5$Jr^QnECx$NdM#&Y9PMGvymB;lOPku~RRFgpSg|7I& zUOxM=7{0N40u*anWuTMOS+9DTFzeU9g?OLN2XFBww_@M;Ro~INRpMwt`6wNC$~JSnVc_ zAUxbEi<6e)<0`PMK~skrD%n8}P|~v{+=b70eN2QwMABK*^jy0Mw1K zz$O(*ny@|TBHMVn|Jb9t7)Z-1W}~z1&XGoqdJ-VlF=g_F80Q`1UbOpu=8~#3Kf&RX zN!6SUw`C&BxSBfCL5$W|&X$TYhgWLJo1;{k_QWe!)^r0uBanQ^oYsPm;nuf?<+$7Y z8S|0eqZyE29rca4jNx{9`oUs!-F#_<^%?WT-igqG?5cY#W2Q%!hY#}nx5?UL45Der z;Wbw7y!EYh%mWFi!P2+7UO_HkU`Fc~15pRBJ3Dh*n;#Hi8+$y2jaOQp(p z=(9ncCO+0Ld^ix4k?#ut{_FAXr96U06@^?ZB^tJNAZm2>%~dZygua9<_ZRr9=fRL>doLQc6mPf=UXg zbSNb)9YczWiiFfq14wsw4Ty9MIpi=(!xT~jlkYy~zMuCw&wug&HE;X^tQK0I zvZ?}dSekt*6}%?7we_;vOi(S3Q@qMHc-;38=q~yGI`15xlPD{_Jv>f*cQJOpRCw3d zfvG3W3nvKoT5!ej8DhDT9(e!V!KIV&P&<;4IVb9fSemN`Z8M96!n%B@G%Uk3EGlB` z(oW2Thrg9+$s+kt|5oL7e0dsynKD--0hO=&-8p|^J1?Sya>{9T>(b@l*dMM1&@P!h zZ_B=~kNq#aMSxj_%a<89&Mx5pGLKZh05HJ#3YkT#ROnnoc6y2Y8wR< zf$5m9w-n$uT8KgZ#4t5(J6!t= z$ZMoO7L2ZkX2fMR5AFXhaHPEJw*j!3VAIvzg;gHog^G9fA5St*w>WFsKPm=_#T&yf z8qB2DJ9JCimM%kAwR1cuDT|&=QgOmXda2Vkp@bA159)lE3Mhu^cti>#4VUCcMNSk( z6;j>ijQ8e2D;FjiFVXr*;8R&PnnOL1$lx#{f-N)xHw?+`a(-YoW~>VK;=9n+*RRT+HKZ3B zWc}b03>kopI^wGNefn-GqKJkiqsv{nNjJ)pLdslfK;kwPyRt}yK2uoe#OD3s?V&6q zS6MOk--5E)2cg?cUJjQFERTD{^E|LzMIrT) z?l5&|5EhahA4LnR%m^3c&_6AH3fAOM!66O{oMA~hsKLfi3W-iS{Q)qkHE_=kyXs@# zj@gIL=V)d%hTQ4w-y*#qNc{C%z(1nV#l^Rdxhaga?_yr}wu00kj@DQ__1b<4=i0pk z%K2KYPuf4zBd)lxBqeZc-?2}VsYjv_N0QQs-c+?ghmjqD={!fR`Syt)Q_%y7OG2=$ z?XgVg>)tBPb-42X8|Lgm2-kWcRf)Btrd1O7RZy<=iF|>Ij78b=GAZNg&{O18Wk}A9 zi;3V;-z&E(8#T$!LjV&(w!$ULbuN6i)$oR`AnBk%!E1JiccTbw+r`hn6x0lnI`6He z9wTET>k&_aN2FVIzeUlVWaDpM{0!79DEY;b{Gd7zCREtbi6m#P*gXZG+xT$Fv+I|t z`)R^}b0uEo>HNjO)}G+jKA}j&zn1xm_c_rE#+3wd9-3rc2^|VXAs{E$Ns?EpDeEuka7dIabZHivy0X3$j zNF3L~?6ge_lrJvDes`V(H+(r6SS-eHPUlgs1OGfP?VyKVBt!nct*|Se4#C_ZD>@lc z$cs;=IQfh)yVFYs&r3KO-RQ8|jn&OAh^6Cz)ctpYH2*vt+D?0o?9+z@-4o^kl%a10 z2TySnpkz@Drjp~69vGRuO||ajzdqy%wOib7 z2PsdjG~$CVI(@swdU156titi+XR4D@G5HkS7>;M`&{vYjfWtcCcpT$WrY=}vCJR2_ zd2$OW<}5##l?{{z|1#)%!)CSyn~SfiR#1B!aN(!R>* z#l7|aMl1rOd(=7tmtrzx-Q<*bQ@=`Ik#cMPWb4lC)xojwrl>_RpgEeaDp)bB<}jw- z3ugn`S&pERA0KRuZ&zvp{K0&Yw+B71GLDAl>+Kga-+2C>s17(gU|npX<{>_<;Su)R z4+W%29bOwVsw({MSY4Q!c@a$;P7Ozs>qRMzeFDqb|JjqgUU% z@2YMawA}P@2=4!*;<}ze-QvyTJkz0t8_u@h0k1DAk#gR)J9Lr9$!uQa8DEI}H@~3e zZw29vfy8ND*z#GrLnA}~w}OGif6ynLkOTuk#len&3!Pc_UVUln0AljG#1oJFPL$*5 z%l3DCvd?NX&*~5IrYyI2XG$D@=NCEcXL|-sZ@6qFt|h3v6kQ?H-c|qeqd(T?x274+ zImDfM>tPyIIXoYLub7|Q>Z?F?YHIhmR$0sy8DKaDuB^0#*k5k(Zt?CtwG|vmt9qr< z=CTY>CO8VZw46aNuN0n)m{;xb8&95QDTHlR>Om9{o>|N$)m;y}mrjZqKFD1U^=7!GSis0Xt?)-a)DJSW(gC;arbueDoxkhmWXzQ~;46cXA61?Hlr$YBlG z7G#`E1hg@)%dosC6_{tb6jDE;S0Gf|cR{!z>4bJyRQ8S06ql+6*aO*$bml9pIer(m zx~3+#pzl6iUJnU$YTSOz^nSL{5d#h2D{L^hw^^0*Em^?urr!9MmzL$Ke-g!Vx>nK0 zMxX${;xp}M<*7NrZJFG(F5@#cx+pbEjd1d-C2#+TqYLe{Ih4S4gPX9=R!_6K_UpWsL?s(;_7&q>ye4{73^-#6fslPY} zWMb;#E)O)zVB|gbYAm6>p(6Y)(}l(>r_`Ef-_QSUwgHL3!*DB2%R3l_`r<@Lq~LvfJdyk-f`l7%ZP~c3$_#dx|xW8_TL?vu845z+Ve4mQnQf_q(mEFO;LS zP%^=P3Iz=#o{auk7rGN$7jD4+gqj1o`1k==`-kscSzfZ$tS1~OTbXKKnI2p6;YQnW zMBIz{`R6qFCPY??`}Ho*=iBeJC5Pe2?=}9}hs?aHuiG*5tCL@O)uJ|l_Pn!uB7I6} zXsInnI%0R)kzFAq+Z|DF4XD=Q;ubQ=GG!(T6^Z{bXZV)Rhd|t!w5m9t0{F7V=1nde zCB@#9?KqsF;!0C0hm7lMN9!3e^ZJof#u=+aVLRK$jLQ-!rbZZ?4)c}Dq-s3&z7LAx{ zfa~Qx4yWH&8#c9NxsTetw*rizG}7>)Q)z(0 z`f^p_xo@x*ABNz)&L)&xx|$;Tj0MS*q?hq}JN6`1)by)-m)FRm@3sSOHB1VS>!h3~ zlq=c}mMW?Qw7zTvC;*ywOUb!5An|>7I0?a_)B1FsFkOOV3z)17QL!i~-hk!G|0t&O zvAl{E&i-J_L>gfJSY?VgQmVb5y;r>mL_+VMmpMp348fnRtO9rp+0J(!0CdjDD(J)U zz1pB*+W&ty;L{VnEsPW-iw=@ z@}5IbCFU&sxmDidLDT6R?uN|~KE?N^SM4u2@^6h@SUr9r{a~wzBzZx_zkHJAz45lY zg7naYIK$YhYFKW^9T8(eSyOyR@VAm$7Y$Y+L;8?`jR!@7(D4g$wL2OsR1)*)%{ z`6#@0L9w$9YZ9Z#;^%t2R>%wR#Qel z7>Ju%4$`pyJTJv$5i$S9q|8{LGtFFDCK89zc(_@TD`eJycIgfcF7m?hT&2IW*MtNV zxpn`}_N&&wpu4R!XcJ!VJpGqW*Tf7pq6(~>?SDjFW*!(7YxDU)efG#Ue0RF3p{fZ2-H|Z1$q5(cYzx{4WiVdJCWU1__MEQWAUcst#c|LFk}U3xB1HYSb@0jO*17h zzJEkmtxMaZNm`fUe&s@(EbqRkzH6xJ*vXY5=!$p1!~tBpbGhd`Uap4RmUZjxoZg9^ zcYH*|`cO7pa*w0Ex7wMqFix!ZZ5%E?XSjBv?d9uqG=xK|+z>W1qi5k1FPka6zwKCV zj0mbbwh6AED)3ngS{VJg9>Kvc+2Y|g^m#F@_l#g>Z?d||4B5x^38wcEDL=`ga@MN^yxdd7b7fhkj* zMJHcN%YeafCp*jM%e6o>n;6x8LO-{i%mYyK5IHA_vp+7xPp;-{Nt(H<_T(y{SX@@o z!=+dWonsbW#kGcRS-qwcVMhU`i*FA1vIc4^Z3&m@$xA_}(Ec42k@_P*KqXyl2KC<( zw4ynuoZMVRMtMAn5l+y#ID6tp&AkX^6rT&g&#`6Tbt{p=>$pZh<5U)tO>@E7n-dSvr>|2eAM=1?GiC$>yKdz+Y%AqYV%w=lK7K_y)~!!Y`7_; z{ixd8liac6?%#;M!^qel6t(H!wYo3G#4O>M`{2~lXHT+K3`43pKOLl$Nu$SioGYY- z4QY-6CGs{x740z3C@DSK%Dm%7yFk=g8qIT_&be?*RYcLTSy%VQ!u`KacumzeUA?@B zKu<0BhG8`7;_A975nck;wjeUxeiGBUgcWDs*W&%hHhj^a<9cP2y4b;TD*Qz?qKP#35`Bm3m|NL^2_k#S?$u309}E7}hOH&G@0 z;)dF~pY3id2<{xRS5#39rA66f)qLeWR$Z-aVNiVc+n+W^D$A1;a zThkcNSL+WkfA58inpUR#Syw&D$}piZMXVB}EpWLQg*^nzrfsr-?@;V#`^Le%b9pQ( zr1zCF8DbdYHXi{|{-Q8St7Lad+`!xPGbz11I~}4`&VNWkLrpn*%qTpnqd7Byd zy3cZpS2A71SaMx%C|`2%SmLx}C{)jHnv~@S!~PVb&Ijz1R3JRWW!jhFq~D-}-PW27 zsq14q)+_76_UDC!3<<_Ot+2yD6UA;@C+Z#EZh4*V97Wyy=~mhneAD9NzoH&R!2|ul zqal9ZRs;)P*QPp-UZ--~R5%^yadgwm8{CqIvmcUKSl+(l!;CyR49uZOIPEtuLLU=$ z$lnAr`XNd4l{b@8FXC^DBA5o={rfIZrA2>DEMsb%`K27T&~J0zlE|*$Q}_as)T80y z<(#aCxrKIfK5Cel$+6X`De|52Jx}+!*rB>vU#I4&5^a4)B-KxH(E-jW=brMIN8haY zWyx%}wPl%UO+cnoNycSN{vtEXzwLF0B+;DMvHzodtIr+D$J3$j19Qp1eT>U;I0Er1 zS*!gy?Q7O3yKUAo+}}4fh9VF*;86z^RA7g;+J{voxX$|eIstf z;JN{LPsBRlebR4}-)-NZu z`Th3xbS0t|*9CL=o^=&KXvqj+b{o z@yw}n(_Ta%A&rfcBc6KN&Yrp*zqZQ zGOOHVQ`2e(*HXmKvb$Dd4;qAA?=4gaioUH7RkDcCmhD-wNiwD3mwjd&A=l&rs#3nc zuW+^rZkx!Cezl(KnB@qYh=xCtJuHB99qmU2IiCvX2thUrg38`UPR^7VWK|{!K@QPO zOayV$y22*nJkHJGEdTiAc3Fh?3uh&M`SOgSQZ;U34(X^*CPNxjtgP}T(pP~g$#9sB zL59EKCiz-FB=AVe_EFGi=oJNvkDW)Aj1HYEyRg+;3VSp{Z)*H2ZXT@Tkwd1}2S>c1 z;gW{Rlurn~99~lW&cW2`W>w2$C5=RgVzciA^=#1P;K!k%!gy}bv_s4%T~dP+@8spa zfGZB*35df9Y-XJJDZz2oOKqr+1>q*2EWgUN>E0o46YluFfp}tBGu-GUII?gbH;iv6 z9#RR!`^Ph}E3Pay%lqxVRl4K|ybegX3Rq=Z0ufDf=DFoy#3D|hV;R4-Yb|rw$W2>z zE)f?Wf(Vg3I;N_EpKg7}o5VU5+tj|T!|xsdVA%&6i5}IRKk8@PP-h%L?ANb*Kn}AQ zeJK7|WjCcVs*B(x$5uBmfHm&`D-+h|V6Zu_S{DA`KN-zPwWx&!45hp%^uwEk5T;LP zcvJFOpyrQ}OW5d+0HN@x5o;VGh6m++cwGOsH#AV%jo1_QQ_{}xj3Ao8H?+V1HsM4u zjkDcqJy48?X%Rr!-njJK-OQ5b7PYw%jSv@(=EL(o5GI}oIn`8JH&}ex&SB+{kY)G7 zT)TNwVqEf=>DK&u)v}d`yfidHLCg()hdu&$IM(U$zm?Z~UH+};^Vn*m`D7PrfCPpQ zrfJ^7{1PSvVyAQd_A$v$ScK8)C?_WKG%L4#K`wx=9N*UNmCGrdK8;kEy`2c5dxt}L ziF9&;PSB|!T*TP$vqE+^`qp~bMW=v56FhKM-z`D(xGmsV~6HvJ6J@;o($mE&t_1%nP z;!f3|9*6X$H}8xw=#=)2LRIX}vTg`|@0H_>-tgvh1yonUGihT}LYY(Sd)yIh@Y`vS zNTM1P={r$W1F)f=;#32Pn0f26bKD#Yd)6|d8Qit`=f*si8xK5xgkQOI<1U9FIR`C_ z>%7-LQXe==t8PFh1)_|~EL>cs>)Qb}DB1lj{*S%T#;_`mA>~swhe)7mxPH(2c8J|v zQruG;sQ&iH>H3E%+fsw^Id7W}w=PZauKQYR7y~Zz^{|D_zJ!4tC>g_$e)^UE3O~Z&tcbbGz)u;*ep1}Q2l^)=qzp7992eZ2`z84@@1z0bw zND!7LlIIU?autJh_N=Y6KMV--G4MZ$1x2+toRyZ`9r&+{?Ypi`9ttjkBXK1Xg-3Czg%6`HkBm z!e&WrTPYB`Q%7oww}n2b_uIYbHtacmIe2ICwvc^!d~pBB3xY!`0Fw*qbe;h=#zh_C zgD+LKQC+0+L15FEuHENMAdCP2mu_#cg@+nBqzBi`cK02>ggD*vS}Be4wUS@K9b@I$ z-O=vVA(gND-vm!sLAN~u_o~ID7oo>wPh%|_K2TQhv;Vl#Ll>fUcE0$y)hs)XL#bxw zW4MU@t8FV{z2!KBzYDsio6D;{K_Hafb!LDmzv4pNncus?X9w@Mz9|tALewg2e#;j3 zv@EZ-Ifux3$;fSa=(5UPC{^w;RvK!k68hk`u35|Rj$mwBWpn&8_ij_C?mVYwQVb6} zU*>J8iEJgu1M0@-b1atQeqsrklIC$_Ehkph^*qylEWFFvo8_Z zmtsD!Ccf_f@_^V|Ti3^$vU;?pTP69Bz!_M#CQ#^9)m|TJ{sSjXqAMXPNjB`YvSbL1 z`~Q2b_4ScTLk7{eWKSj9^3*&^`ne~aVN+pa)ra`XbCDq)N>?UTm?JPSQ;14;qm6C! zZA3qF)09^cR9IqQu`GpRp77MTp5z@_E_5~H2k zj+ozvhW(uM>}!oW==L<UXeE6WB zGkZn`RM26y(*AMQBlFgi-HH7SQPXKPymEYpp)x4yx)ZudFApc5OLm$=FLfhBJSRz- zqJ5GGi^F3)2Yd3)7~wK<#elE=jXmzWaQ$h=DE-XV-vvx+VD6v4FO52k=SD z(LobLaj2s}t^VD)?Q}_*R+`&v5~LYb>-H?XGS7f>g4NW4)LDp+rXBWs1FzGPkhq`Y zQ}i-k4ytC-o*rpU9F-Q;=W_%iZ&4{&Mhu-g09Wey&q`24t)JVkg9bk?I`<*Az?6w-U!2X7+tl`c$smX5;Qo|Tg+`#=_q zXP`(&(1CC6q(MIo&e?9`oa?G$T4mAi5X0r{!FF%4RpdW`zih*(CI7uIs-#o^t3QMZ zBM#GTLAP_c-^gZ=XIQrSnyR+?I;AE=`oFM&g>pmSi-BGn(z?wyQ{{1?C;ilH#}-+> z10v4p$)EuQQoIU1bVq(qz8rS!&&c@nv?(_;oTS!UDU8^h-q&9VL&?o6Gt>Q678 zzp&wc4(~J8o5o~}v#K2Sa|0U}{f*Iw!US|vrI3|+4>p+dj+P%Ji?*?3T%OqcpNFq8 zlDnsLkvmy5z!DdL4GHYY4OBwR_dE^+MR?R3L+Hf?<%RG;O<~68iUO__<`ghf2ovSK z-?72Lzu5%mHl>+jzD-&7RRrJ1&EaQ+MVr`WdQ8B zXpq1JC~F|1hw**EB@2oPah4?ybkg}W-=tQ?$6~vJ{f_MVH*0#PP(2w=Ba4yWecmn# z;|h<#dGJbg^Y1bq(?i22jp2R8db~+a8(gFnlYDb&C;@o`MeypB)mduRKOG2hWk`Wj zj$ryeMWJo=27jSnHCSoo`ACHAo_ckcRj`V2mPSYPCU+Nx=x)rJymxP^+G1^W8|N#5s3g!lb$^lSMQ1O5S?tepE-5 z8j)SaiRq>&C-;N>j>b0mVOHF)#~wB0)lgH>vMIi$b-%zdC`Bdp`JV?^)j#N`C3<{? z^n^6I8WwAgRf%K$v?mev^$E^LzB!Z6rrhJyNDr0??B1lGj0tusIFzl&d7&;UGv3o4 z=k=CsRqZTwmncdo0uRJ-nq}}FcjYOi8%j$q7Xc)ulj6Y!dd@xLdCEwpEF(_*I9-C5 z++copsiPIQ4EI=8VRp;QcY2pCv&$)J`R4^jQI{CtpD-*}u-$YS7wa6O@^J4z)9Z(O#UF-bS?n)4lk>^0w8RCE+t$RZ>8Yn;nM#D3R*{ z2I9n^&ImZ45wUY#FJO4sq+50rZ0l5QJ4#4O>;{XP2#%;iod2mN|95VyzEd5~)1nZS zsJPJNMmfIlUT>IoZ_Z12T*6c0Fg({ymg;=0bJdj3;k)JsmG<307>&V|LZUiwpaq=2 z-DD$ee(l2U&DWe!*&?-}o374lO|CBko1-1=LGG{l3Z26A$^ih`DSM7mq*R%&6zUfK zc0*ws^Z$5A7Zx)3k}s~#1h+=Bk?C+QS?b3jV-g`M9gq$~V$jRrmkBAfclNB%XA2tf zijQ$$6BQ+it)3m+en{IXxf`@st~|Beu)LWHKnqhYhiqw?qb->C$K`G6(onjn#KG{) z#6@jbOT9*W+EqQbT*}1ESTvK~uPl|5)GglDVLHv6(NJV4$~wWZFEEp(BZ3ed51XWr zP+lblG*{4asdop=Mnh4(wYB$3;yjg)eyQ*fhcOGtNP}u?C$5^87|m@dr2w>FvovU( zLL>k`V5YwjjSozqm9L2$EIGLAMQL|$vc9POXdz1#wmoh7U6ZaVQaINjxh!4IbVbOXco(ci^p&g@{mVmHC;d2|js5$gSL)ogkha)%a7Vn7 zpUg07V36B4p6#u8vWIDoVpdpvwR!t8^Lw$YFU&5o0Tfj%IW%p$=sNT_)EUq7St=ur zh82evj#_dd1L(=BzD)7w(HCh_r3CXPC4l-bZD{DdZ6V4{H_oP8XnkgwQdru~!`N<$QlpofqlKTOk_Wd8b%t-|n-@#+ zHa!dLIJOkWB10^ML;>`5uU3j;!%sY`DsN>qvUUnPaItXKZe(M`6F`mQ**@QWE-b_w zwjZxdBs~*NG@;Qye#K@jLE6gdu!yP+;D-I@h;eW{H@;_9;;C{58n%Vs3&gFa7XOxe zvoOT?ex+&V5dT^0%gy7R27(p^no>m7p2{16QuK!2L!G_W*jAMl?HJC{`h4wyd(;78 z;HS>rY%glo+!;m(em`V@eBU~>IQyaSHh|x&`{$g%S_0P})zHYy$<)! zG1bqgt1+$VN?b6G@njHR(ulAqi3+puVz}I%u{NTi zYvH&1vxI@Ewplj0ke-!GftX-2Zm?lPnR2~xhq~MlSR*~XzkknJsX<<*;6>A=ilMy- zxEUBezt%>pS=)iNT!_X z_J(z^IHtZTa^d3GAP*JLRQbxD2L0UsK3#+oe;GMnkf0)v8!Dr7unhwjcT2Aqu6_HU zoHn6)Gs3Dk+q#?(GaOr_TQjQs(Wvyv8Y~sEd=kucxVjh|Q06u+QD&}7ld2deI()>) zsFuZPoq21Fw=~RYwj<(cZjNn0EfI*`1trZq@Rs`X@O)si8%V!7dRy$;g65_1*ARYRuPh{I#k z%FPYg9xBibW!pU>UmI_Lv3$Fq8-5$t1+0GoGopQ_9rMqDUz9(uXQ2ya#uM^m8>4n4!P1jDZ_5)MsbCFZLV0!&-#R{05*?JKTB)c^bTzcNG{1u!&*;xUX+ z+bWxa;0ei^=>#UhiOte>q;ODWM>AI<6674^9YEzBB zeYSmxz!A{>Nad6E^F6vLdSlGHH6BiIZ(b>rEVqA?lC;s0^cVrQx6TaWd!aI?Fr~ky zt64st6$gCXD>yuMc9?IA=E6{pJ4H;2&1^VMOP4K-uhH(FG!xg^qq4;kJpf50dc8)- z&=SJP!k(xz7dXLUHa|Y{(+gB{5h0c57DqKuuagC5bAZ_A!*Jn zZG=a~vytcV+PdT7G^^B~(V8lCO>SE#h$oE~O4XKmJ8e6KP9U`6bCetshF4nOPURl_ z5z19s{rQ+fj9kzb54CRd(|2WNb(A_T4ddHm1j8hESP>Dx+w zuVUD#f$LoRlXgFB%3im^f1s~XN7qh6(Lx8i3kj82Y&$VL_;nr*++!+cD+mg}1xT28 zhc-4;ab98@1A~YN&$+qE7mvFkXUT)!h$%eIH~eX3LiNrWu`&His$Lptx;_k~`H=tw zR7QX%`6!7>f|svw@2$UXv?T|mjkcZ*i$6O8#lS0cGU>7Exr9^#pq=}`@IdXy0T z6un@p-Vn{0{3gVPdAHTi)3xxBMaBTfH)H_0rm9;^r<+q_RHwoid7#mM%<8Z=&~x=Ns*Tr^(&k7I=(rMFV6y?#unD`XJ=BiZgi= z=Q{v~Za#}VYE^=IbNDJOFNx~&9{&oA}0dfKS!CZA)qe_9h%t&?zP-SRQK8MLTd z^~QIT6}X8}Uhzpl#Y}4z;B%1TBISf;^BL~fFDx1^oz2rwsepYY*tXR>W>*Utp1ril zoc{20?yI7K23rKq7>$8pn1G_WMmA2VJsUKw81O^ha^c*>EfvqAvoW_KttBp$Ew7T1X zYgcA#qz&mVzrQ@3d}?nHeXil*RPgi8dm|SMm!aFW=!JGeom7!E>866m@uO+Sj8MC; zcTJUb=`{B2?CtIQnNPcaf1L1nHTQlqtXf9Ru$$ys-_%KdM;BYgSF_e1dp0I1`f@{V zk<&7;TyjQ8(BaK&Wg-L=+|65NTu@nQQE$Y|`xVSY9nkT6fvmivyCo#i8~UoM2gl$Z zZHeugURB1yVwkyalG%&3pwE8cwmmEu*smwQxj$&thlCl88=Phhzbq{RT; zM+uNJd<7|C=RO)$A_Pr{DOKq@qCg*-s8zlct2YmsWEvxGuB?`n`06vAbe4O#xphj|ZQ>U83&y!KTSuoagDy6`8C#L#(^HZ<1d>%aW_g1_0mz zA1)#ufd{Zl;;*a-o43y2=Kfpm{~loz(LF#D^&krH2SQCaWIg{jZFF7le7xG}QgGB+ zR3R0->BLC0K$=?JE)U5VIk6<9RFNYxIwyZFzuEbtaU za+y0Pi7pfcRPCKOJyUtCEf6kuh*SG)?$ssq-Q1gcKZ0$ks4!Qo`=o9xbW8P=vMMD_ zZoBz!Cj`GXnWrosaL*x6T`iC=7evodSQ1972t&Y;%{Hd7fJo;t5eS*kA9P8W11V#H zU3xJ}d`H+0TyxrBGi+{M#GHA#n_eTanHF{-9obK-^dk^S7Or8^J?*goc52WBj>>Xh zi};oTKZ|2mnoxM5wlG(zR3>g}J-4LbxYQ!Oi3wayK0PfkfOOS`977L!9@12@bTt`B zqh(#tYFUmU%ut(&}w>p#-BI%HNH z!`@5Kg=(Rg2LjR!)!MUNsDrs_FHHkd04*2cK-3}8r6bH5y#Dd(tmdj49^b18Q&v(% zCH*`*5TdR0TyA`Ple9)LcR}Q4BqLv zN=!UnImrC^i~6S^qQ3rdJLel>*?M?jd1li{Ch~ONF)wIs1x`ukh2E!#qW(nhB5g%1WbT(L1Xi!`=u@m4 z;EMPzyjq$+N-l_NhMVbq!aegBO>a(I0;lYB$rV0f;J9#tpwR*Lm>@53CP#5QW)RnJFLR`W%qyH0hP2`9Y)X#ca zx7H6aA*{fyIp7r%EGm+)<{^UQz3efv~blI5_qI4sYa-tqH~|HA?x#!uH8T?d&GPP(fD4}miLT>I1A$)herD#&F(-(Z{mg-5lQd7#_`O$?e!WP7lcNm)~MMk*g0hs zEPjf^XtNM5@+R9pZ?I(?l($e{H+3tdo4u6x!QM8Jzwy zOE%Ixu8FxyJCaTO{tTwYvWZen^Lo0Vz{$@)X^es27Suqv#4V1v9g+8i9zeO6pO z5km=K3727Km*vwOtQvJMuj&Xr>QyqZE9szEH4_nwk7YVCxF3A4LEu$y@$2i;3p0gI z7EQ5H&sv%)i^3qll9#8@2HpJy{}bO3yfeO{MuW-bIXJ5wf4^q_CR}Qfqco3QR(5hu zA`B2a9ilFh5`OTrNt7L7UN)A|Zm5=U6 zk`1;yiSg%U$#mqpGGejg(5`--C3Z+CRr2n7wq?^x1rbh@A0wx&{(YXo&inJzb;N$- za=)vK9jL@HO2B6^>(&WZ+aWPX=+mssi8gbWhg2*mWb&`v+b&yOuWGt>On5J|eiyvt z5_{sW)u7=zwT<%Znq?1B@I}gqZe+4rZuE3N2=?!xb(v}Venaxbki|ggmg%L+qK4!5 zTOO`wVdD=VkU=K0f;TKIVTg=h74hGjmD2beQ>FJ2t3FgcZws#G7kg1&xC4XW$6*F! zb3YE@{a6;b!FTPov`teK%M;MB7^R4fNfZI*&EH%kY@6>W}iCfroSmb1J6 zb>)ctU#|`Wf&5fgX7jvznyjmp10d<6z^^^q|AdI%dRt=tH*p{fWUkvbUk=&d){TBB zlrFXew5ht!pBMUdV>#R`sq>94{LY}#GdmL|S<#~K#aD6cFn|ELF#{X0^f=ls3Dnjl z2fI_1cLHG#r>Rp6m6umIE_A)#yeqogNUz}P0W3fKEq?##iy&aybP>nN`D7Vo+!5}3?_5a$W zJ)~pnkns!qHc)*Su4+%Odiue%a!>QarI8uKGm7=EFlOR^c$(8V;g?#f9Um| zXQCVQFamrJeH>W%>*=^ynbx2~!l-A3ea8OW^D?7`X6t@wz>jzqRWxdTe7m8g%a9`z&L@kZQ7-S93aI>8_Cm^2-^2|eSmNi)e?;2an z=!XodXQh`z_ku+iZa;$yGx=Z;qg2Tt>3qo!82Ti1?UXS=B-r`N%-2M$&xlwAM#A~$ zqn;pECW&FjBB#DvF375~g$x$3q>UtD-&ApBRV5tNXZmr_VO{c(YeE>MMs#+ia-!Lupr3 z6DGV925^gkVgJ>+YpmuvHlSmzU_u6475+_Tl@;%*7P6pAc|(C0%7BAf%EY#{@3K9? z?&0(BWpGOWc;-+OWQnMvE%48M`@b-p43TH;e(Yz+6!rr`Ex2kdZZ#^#)EZ&K6Y3Co zhVY7u>>B|~=3b5nE1a#Ee^wlR*d%sppYzz#dVsJ>+0t*K~ zUGj3BA{(Qr)Apng?v5Z^ST#%srl`&IY&P;4cgOPIWq%FLPG}ID!Bfoi;Ms|UlMC6g zkvO5w?P*K72Y@Uc#uN1=GkC}9OSlPc6@RlBt)vY@ zB%?wl<_&PZ4<9~E#u$TdS3g(K9*83vJB^W6y+Gp=rR=FP4JNL-IXAxEewK^y>h5*; zRBq=eJ3IE3qjXn-qwy`OO5QS~Xytgf&ARaItKhKHXQK8g5|_GxC`YdM8}&-3#P&K% zwp_joxH>@P3ZS+-_yG{vjkey0VKy}!cGD$+ETSm8JakZi8X-F;tc|FT!vJg_f#o9l z&mmCH^NZ<(2FjXfEhF_>?@0v(C-Fz4=2cl#4lOT{C2$_3L%BfeH{p3 zypIF_*SfzWl(D0YXJUVPXMNWEZC=i^zm2Lz0cD7 z<09g!38e1O0$11q9|5W>Mz7o{u2Ep`t>Ob1Cq~Ii$m?>%qB((37lTdn&J7o1)r&lZ zQY)n@^u&C%ft6@4+BT+dPEgKPe*IIuzg4xRvEuwkb?bpf(j#217>c%z5}Opid?>; zTaG(=(Cm*2SnP66U=_P+UY_#)gMbrCCxK!z9pi*uuCoWDSDJJ+^(P~pVAEr%F5{nS8qRzN~Lx|-#;TA71@4bEPL8#lj=i%sa{yB`S zx(U@_c%*lhJ&af8>Q+=&OqCdj?h;#Kz5xIE5XU1ksg4K`Hio5NH|SBgTk!rE56!%8 zR^)028N~cFbmhJqJNliI$YG_Gh1JyR#he-SpMXu*@UdK%-wSzDbaNzCHiDrD9sTnt zbN%XJ;PwQ!QWeWR8ak?ge-$7*Y;Er|54+WOKFXAQQ+THbi9iC|>-C+s#b2h;wX9fip zHu~Xpb4E1PYNC%*mYuC}<7 zc>~|^_7C!a@|aOq3*{{0#bkJ4%fOq##l7|Qc(Y7S2FV5B<9t&JL2QV9!{ELu`lCjd zap8Y!eIZbNN}#^RmZk+dKYUU|6cUJuve<_@SXPgJMJ_2@^=Km6{XHdZ$e`wG9GAlM z9xxj`Bc1g+Bu=s{99_5UF)eCRylg`w5iO!V+x&|?>geXZT-@p|R{G3O91C|ZiB$*8 zXD!~vt~V#0K)dC%=#nOK-LX6MioeZZ$vHL-<-CL=;eT!%5f#XEL(S3t$lb6vzjS@h z-5I7Ea!sqxwi)9U68#GZ+h*g>sT<^gYN6QURaOz6S1RxPB{CsqyxRpghH`E53q%Xg zrZZn1SWUnHepV(D^=2~PY0yCTqdTNl0%84KvrPtOZ8-aDs*yHvugx_=0(aP)&ZpA> zEIXN7-C+OUNCI%#QIY5A^iIOg-&P5peGg>V%dStND+7jEla5~so3*>AswlUWTCh4R zrOWGpB}CT+4U)mOA==@P!-iyT(1uQ7_wn0blbo3y(;9V!gtSAr#?Vgmy7l>R!lBGeE5ls>=s0>Ip&L!2|% z{!@0CKes!P^sV~l5)k{5Y0q)WNJ{Mg4^%`|-nsIUuZHWZl51L#YHI37|J8wOuIF3- z_bPVOFa@)a#NjG%4Qa6LHO68Ng0Vvio0TSZo*tm!G63RDtZum_V@~VtlVRg;=02_~ zF=D}%w{HBa6W+Wwc{%3f0LF16!-kzj_Lm{CDfWa@uOCu3C0B<+3slK|ngW@7NBBO! zBp#B({Z|<=0NSn(OdZ(*#MUtVQsA%u`$aEbdjRZM=TZw8+anVsAa}&RRt~T~(r@`c z<&}E#bz$S$Nc4)Y@bb8*`1QNSpYT&I-s@bn_4Pf~2+$n~>?i4;7P1n4%?&f!8B&{L zI?uP8N3^s<-ttNrgS-D7qNtBm2gnqak;+PM?zer733!&9!&bTw_3q@4-KEn88XkWV zw$4pk7r)}SEI~5RfJc(xpppQj{uHx=Qa#l@bsTupqs6ln$YWP(lcbH0iyB zD!n9u012Jn@qXWT%lqE%-pl-F{xjdq>kQ6;oSn1J-fOS5_S$Pbk9(1o|DBX@$pOk= z=SIeZx-CIO=N%P!4B}v};y@=RHG?+){P{CP6$yUy_qd=Yy--$#V`oJ{zTgUj1T?=*4(2vgI=@XCXn@?7~eE3x;%kyQ~+W$ z;83&|O3?!$ey6|0Rk|pcN>aLjNsA!rz!EtCAUJofl>W5%D|b+~=97)Ba|^qhR{^(| zBiOq4(wUR9wJr-cFZN4=s{UuZe>&TK@pZ~Vro78iH26Yz+uImX`=s^W-ieO}d?bOY ziwa+!5(VnaTe3&4`?1Q~w4y0+r(%{gx87YQ5I!y%g+`ygFZ0 zDejyPSZCen|57mK_)uyKs8|z{ayNiHYfg4Fa8zEAN~hJm!q1W8)4DRsOkk$xTOs}N zfSLM>FRdk1+nLs1Jpf&~1pA)u>>?ZaSy-gyJpd7B{QUEI&$`x!iptf*sNY8JU#?`C zDdMF|mxF#Oe)*rx0#Fine108}L70*_DocFp6YxJhk=LSImD_`0@k)|2wgMAgT~60&^VP;&mqi`4PbJ{O2DbbyuD z*21=aYu=3nK$@3M81A0^Ra5*n?Y~|zwUP{SKaw))sdd_%Ni>U+J72K_z8Jqp$=(KT z8|J;w`WtUTKrEneKbE zA7z8a3IzGKY)wiOHPzTFUVe6^p}N7QZ7q$h3bl(Dp71m;9jfiTM7-uw~M ztrs!ee>FHjPx$p^f{2T*aokq%aE|_PfzUR4RY}0}r=^!wWSx7nS$o;OpPzl~0PDZG z7_J&QH87m{`Au1U1-9|$x9_ES*fi2{PSFblc{!JqOOamp6Pn25fxGgfZg+w&E=j$F zm;I_)fC#kIjF=+by3P$DL0~o!UAgqz z`-NUC6Z9aj?Nj{)3-(V}|F1Xy?$dzvJAk(Ks4wdLh3@ZnpZwX~zx^%X#!aBzf1dZR z1NNWs{F9XnsHy+R%l|Si|NpN)is+;m!*VMlE7S1*O4|B0F82om_oqgWm2@y)OYCZC z{Bw4GIV&+y>fe<3pTFO48snEMAYZ3#Ff3Bn{N{lD#CFi1J>#$bc9DkJ>%TxEPtoUI zfM3W%&H{>{SvqbnvGz>B;zZq(ZN*S756z6{`=Fg^FTfofH(ho)1QvcS!OTL zU-sr-ItTnJ^Pgq*uOs%KW%hgF`aiVHnCOXqajWKR*=-Ck1TpX+%XVX)(Pm4(|rZX@5E z4Se1MJH-vKXGUWiPGt3pAF~5h-!FJH8+=7={n~H$=XS-pj9Df;EHVB{Cw>pebNS~w zWQ8m8_BrOu%^9&(s5z~>L-!17tTF~lOq!Iv^35&Tb(jVVIxuu}p7fjL<7HcKdZ5*4 zNsiEn^zHo88#Gpd+dMz|XM3jW@SauYV=upr_h;yw#-Ro+jJ#fr0jawUr!~keJ_a+zGW0VA6#r(@2|1V#KC`Bgz z(rZ=D@7oIHxTfedoaG7Y6PG8-@5eLxWVD=>(I~xs9Nbe{}U4y z$8l41pGn$y6@Y2=fWdwJG-LdkDH1)rF6oDZZZlS@cHd%!lZ6~a&YEwN2X=0dSv8RX zAZ5(DvXR08#A0}~Po9nN1MGKtq7{G-6|a$jr&m%m4wX8E-w@DAaWUELRc+UmZiJz3 znW|vL7a>uHw781er>)04mDDCjhZ>nD)^jzah$en{*-0rFlzb?Aac8pHwHyfK7G_I2p%;_XzRVN^E$pp0q8J@^xt@9Ik zthEqViK|SKi*@ubv^LiJM=2j(i!^2(9fYS!xGO#*FvdArg^PDqPF;-|k8Qkm$pvR46K zOezoubrfB4Lfl)|>X(8Bh=s`321I2C5s%;Q5pfc()nl{T*v(?r*G1*MOaD%Eg-d|5 zapuqm;z#P~NVTaGry$Dvwgj}_aOO97N#PyHYHXyjDgC4nj(5y!CJ|m6=9QVH!%IE; z$a8y_eFeXIHbQLDV8c))L(HGjAA;ibOtwGI0iHe{5Z#^y@8swOe|=Sj2o3KT%@AoP znp^F8py6Pg^G2vOnUYlO$YSIJ#0a*Zmi(jgI5XfqVcN4)gK0j#5`2hoD{xyK9CN~K zP0S{%t4~%BG(^z~@wfnsNQ&Ea@sNW>sezM>ipOnrlI{bYP_%VA&HRgW21aqbfY)qu z!h~8HdWW^QQsKQ`_i@OMJp?U%{FteFXLW9HP4U1(ze5~bzwjteJj0h?aq8tj<#AG8 zMzkryY0IYQOiYHql)559-0&}Jq5N@^4JQCqcgRh+gJKzT4WS@d>nDKNjoFzUPt(GDTB)ddMO@BMJ^nP#Wu!y z3-gj@cZ_t;%iw`mff5MYlvGhVc0=1xy;eEi>E4s`n#829pa;ffRvBE6D<2bkKv>g~ zZ&^@4BP^?z092j5wEhaqjvPHmvQ+EpFodN@pPP_hEg+sQ@uL5eQ~^E67y8~x)7NE& zDe3Ohk0uKQgLAh~p38SMB=}^Uumh-e3NPF|2@)+(>C*fz&PBdDOg)2ozv-vbKk;HMbKT!Ai%;zdv@%WqT4UH$ zr}_eT*!9hj%3>%yiddtLF)J$z(^N3!w=?v#omjR`oSuK)l19i53SX^vTH{BtlLWe5 z#-;mUVvr}tD3fO4Un94FKdUp$6x%e+Lh!j7GhrXqhn|4$nj1`$a%~~H%j1rpq@&j7 zWVL*^5Tl!NxgDo=VKUSrtNrC}S0K8-{DmB>2_zoA3+=ngp%na}X#DoV=-UDQtseUC zX&+oBQoH)A)l?gEV3It^;o=Hk7kpv@54yNRcI}fo zdc6OzUnLvJ!s;)!)n>c;sg=YkPdVy~dVG^}UeAUUm%izkAubh^9CjzM%y>Ut(r13M z54#7205Sx17?T4^BBKfh8Ymr?cuGNZJiq~eRK3^MLsiu<_sJi~qouBr+W8^D!4KYd z@K&mWL|b2}5q2g~1J<3}DI~ZMy1Iu=#5<5D{iQ$#8oIn3GhlJJ`&KJYojyYBR1BU* zOg3kk=OQwqO)i8R%nhHd1dY_XQ0+P{{!AX8(4X@$!gGq|cJn*R7yv|pH%-ihg|>K^hmund8t*$? z=WNaazifbF2|=>X&b}>GDVnTqI1>Xgs){B|`;QQfvJzEJ4X^3h_I-@g@Hc`vL_Lh- zcmNz%7q60U*APV)lkuJc5)b{G06Ih1uQvNaT&)5JXu0wUW@Zw1lK^G0s$zc90E+6PrQ zqJ-H^>Y%#xl$5unk9N~;hvbu#39x9a85Dr1ESoq)Ot}FGHp@uzz%Sv6u0#;v^xn^*E}o9-amjW z2WnONG8bG;E4x*N#2eI>D0F@2e~O`sU|!XlPCirdu07oV2>{VMF6Ksjd*mfKzVJGY ztVk~oC=`oz&Q*^oMW%BBHZyr0YUa0~J63KUdWlx>?u^S=yT>!yYj*=)@Ap2D<%WA= z;{nDg=vk5x=g}u0ntcpx9X}zm1eJs;eVT|BG9|1cet&$onf6G+RkZKg;QJJihw`0KisDJ0d^YBq# zz2ZcuvuD*Mzhi?W5$6(L5&Mt{4|@%y>&eC^jlLAQBlT_Nvs}O-W$xR6k6uJoumAcAgN5KpX8xV$Ol%pGqV#G_tlpvic!GgAA50xv?;*j3FZBd4TD4|20d>Hs>J z{~}6a_~b9%+KQu zld49<r-wU{^l zv9TJ=Y<_e2wu!!XLC$OD2q}q~I1^L5Nme=U9udDFx%xg?MzPCunTG{i$VPi9Y1qa6 zNbBQeklUg48Z#%8Rzhbwyx(&S;FC$EghQCRWJXd>dZY?;vm#q()!@3szJa^GV-rJJ;ad44Yf*VTM{)0B zhD@7*KRVJB!ZE6rx};!FXh#}dR>>;UBoPTn2qt~~FfRXY5t5SiU=AbSdb^v5m_f>0 z>w70|M1Ppu_WWHtiaE7}_^Tk=`r`#@i|MC5>o3WUC)bGNsCX0g1)ukt01aJwOnrTRgK8xr$-OKnosHsv4jxm!1=sjcJBWbh`Kj!zT- za3dzsXA<*NsoHO=90M$t;g5lQo>t%~3oQUR*QXE=M5!mex(%QuV1pP|yYE+t93O7+ z+LO5WWc(hK=I`8xMk?hnDyV(nvX{(KVgH(1Pma5g{2W|hq=RKd?}b%=`2*X%7b`r# z9@vcrO}jw$aE?PEb^nLcJc;Fbfk%Icz&byv0$cBk^~pT&+?&Ow;l|`Y%m-E$IfO2B zac7yTxY{2)``IgPfI}f<&q8-FEUw3|{9&CCFK68?Je#!Rr9_kdl{=Jl$&a>TN!;8L z+PA9P(X_s{g|2BPtcSCg6ATS_Ceea5TgT*asfB^8_Xf?_X=jmBW~e7cMcVLq-{ab) zd?BBw)pXAB@PkRq_%9_-xqT2%X62IXqv&7C* zYNJ3Fw=STeq7XyrI@WKATXzjY%b&@>)>LL_$Sh^ahvt|e#nS-H*LPIx#-p}Pzm$gj zuS?;NAIwl)T$ND(r@`K9Jv#VG;N=t@?we7;TV58ygPDy!O1$M2 z5B2UmYBZekZt7-XI9gVBYd;H6EI64B-1`Q2%atsqTVJprX*h%upHHnKq{bL_CKO9v zbr|-Yw_N>#Hbg(lYmq5Pk!3JSP-bmqI&qvqF^H#U`E1<9Z*zPdM?>gXF z&Ffn`v+~#ZleqlN(EfW^+67YY)Pyt-!)m7=Zt@n%yb;ub<$qM(f)-|^1ysDt7U z_xxWDN)ryrK3+yleOK^*JjU#9u#7HGFa6K}#zy1m$KMi@bf+J^SKarnP?8St0J15U z2%4?oBL+JoX448lNrR=mk$`$UuUR)+qt-bdZ82xG@;S~@1T4mPcUDOci9ay~+@rlC zf>(dd1C}v_PqnWI0NoYY>bkQ!B#A#&u*@OA7_V3+B|TB_ZaOO>v7)7*$Fg6j!OzFh zdK!_XV!y^eHYFSAk#fwsmTh7<%;9uiXZ!$P$>S>7%;k7g)n1mLM2}Fhtc2nhgx-q} zjR?Mr{(gSz9su?T{Xoo~t5jgd{#;SA>tik0`Ady6{9~XEUKv~A;gjmiXI*JS+{t3~ z;5inE@!+%sZ|;eDWl(b+D7>eNi;zBM>S{vwHl!J&3O@Dh)p5CHe(ZSK+QLed19KSr zl9yza;k%f$G=F@7;o%F4Y>#L*4F&*9xJ1L>qdD+A*}B@=UO|9{_xgo#+rIBOnb6{W z2FbejR*l;{$nG@GrjQSjeCAR+B=|wt4|iPQ9g*LWH=$MP6t<8nVEb)_c!X7QdATD( zONym8r)}AgO+B=Mr26L z(5&YMgz)a<&x*|SN@<|N>Wk9ndtz{#<4gNK4M=%Q_A3ncG+(T=oEx@X72L;oKM3!F zpR|?~eJ^)|5jNK=N;?$&cV~S*j?rjq*SW(#6YV2bxkS)X)fonQ@P~2VFdPx+1x~%k z2SBu>@muHei(Y%BgW9d|Sj#-$g@r6rYc`A>tn~+G6s6<1F|d_z^S~cDPWbsa*UpBN z9FvQ>16z=`uOw!>pb$4&FUPR!e4XYi4y8@&zfc+fU8L{77kM-g>(@>>zX@ou!>Mmk z0>fT|BOlmrnMwEQ)R~#`Jdw#JW}51)FZMmyZtg$)_>5s`O+9KuGwIMmpiea_ph9eQ zmzTfeKGNHWn`RJ_JY5{_Zcb`+w47M464yV=UuY(2Z{UPApE{sTO^W__CevJvVkypD zW#5qj&dV2LiC&iDpX@}2w&SiZn*xM-cHAmbv*j&6LW+I|o#Anik{Y?i&b={1u{ywg zIFpy3bXn-`bNo6ZFhaZ!rs_#LpgtLznKFKRhn3|nD2OEDvcZA4=PirZVQ=0#q_JcLbeklj{p!qLFUh%WpBBU7)QcmQn*ycfRypv`Z z?{pchlkj-lK*6rhKeWzh{T-7zU9Up z?OdAVb#Y%~7+v0{R*j_MklJ~$Y+Oz)|xYmQ0+6)1L_M=63qDBzuuWzQ7ihuhYt`hH0ZDouZq z)fAeeJbQDo?$H(CUs*>@&AqXDuS$cGx_p3fqSHCI;}sj6JP~QHy4ZfM!d$8NYb#?_ zi2+a+)1EQy*blxz)}Dl40D;SXznY>V6*mBg5X``4v9}xKiI^ z42Yy*^!CE=Z2#q9(x1)jE^tuZ&r25f&!_#DM^Ie)k$}-;@+|C>4QKR$d=Dg-&S};( z@Ccc^7lBlkOP+7}PTBC)k3{mL3eC5FbOL^Pi4A`|=B#>Q;mO>tMjaeZv~umPY?+a- zD#gzVvdx$lU!5|UqoRn!>vv#@cdlH~didauvWjQx$sy)BCOrvJ39l3vhgbS`uT^!X zr7Sf#H*T$uO#j7(Kfr>5>0;6){kKx5d%yZ&+r#-#a*$%2VY;Iz$Xh%U;pHtBwhIHi10+>xQ+j|@&aWwS^?9+_iiIr@}N z5y<=Tf)}qbU8HIIqI$|^%EV^^BiopJdxC zY94O?{LkYmzo?G0=P$|;SKK{aYU~-JXjx#|`HgSyzxk7O^H)kH0GNk!28n;R-2SRr zI}nZeRmjK%MPiLd?(rJAIGw8VfQ$4bz?VddAyobkU-`E-{S47}*2vy>k+2canqGN~ z?`f5NG+Cz^l%uu#)w@Vh#h!rE>h2<74Oeos;ZOUr=0H2wu=I1M%r=$L19X&?ZY27& z{cHtn%;I&)3#av%iUA$mS;&MsZ9l_FfOdS|!%di3JyS-76m%|uIS<)^;nb@XxF+ddiRtz%|aK@40`9s0K(3HAlkp(;-U()3-5D% za=K;|ZH0g_EGDgxJY_%U6M=R&J}ot#R;+CFC#K^a@Yr z8D)^PVo2CTcs>5;8Mep|FIpSE$4s%Ze-|Lj7-P$Psstw3>^@3qC|Zwbd*;l5y&VxC z{o5k_^DCx^AG*+-I(Fcf4Nn`x83@!$KAe`nZZ%uX&%Mg;gnGYJ&=iCECSFrgq&A|_ zA~WA`bira#Iwku-mzGw(j1<;5dMAQg9RKb65GvaM((f|*wB3`+YsK(Zh93q~4zhw{ zZ$ftOdCia%h%xM`_ftl*XG?mnP!(vw%3`@6-5ILA4Fs{=c-pNr{Qt^w1X%o>D1788 z;NWp5lmgOY*wshw>^R~DUN-XJ-w?1#X{3yCW0h^Yg?R1;l#fOmJvrJ3@%w+48-u=x ziRCm?r)BI@ei(ZPlQx(vWN+D%E~y1D@y<)NOAPwZ1%_Rc8P^`1Evvec;^@?<99mFy z=AmbUi3mf6nTEk_f5`YEy3lUyrpsr(Apt0Ab}qiX@B_TweNBgh)w;Tr1LcX18DM0< zf){%{6MbJyKhi4KnZ1POt%htLd{^tYZWBB?44Lj@W;Ss%VY&Rc@bQ^Gdz~UVK3n5T zi}~+mSYWhXr(HLz;CeFsppFH6k}Wb{1tu)}JS-X?kHmKz8Ro$qi_?adFbM|sh%P}N zU-Zh({ps5)RhzCOZ$reZC_DWVx5XjpB9nxL6@K*0VV;jrf_fLn%dMErh}3%t%|+E6 z)G_+r#?53)37l$gGJZ!t^(Q7J5`JEgNYrf^W$WRxF{7Eq3Xcw98$jiHI&U2`4|Ahg zy)ZFM7CbfCz&UDkA2KW|nCv(j$oTkneC|T_i#f&q{73S(gB#D;jbN&|2|YDihy6|LiB9 z1NqP_2KumoPtcH#G$ffhcstK%_2cp z8IxZD+@j+Bla+ea@a56f{;k*JT8%`=n?|N}R8dPUgif*=Y)lvt4GX6d3^M(Vh*_S} zN$6U^^xPOgeZq*Bva<|KZWVeUUB5g3W{))yF|hVd?-SZHxh(|Zb4ZcRb6v{Mpr4r8 zb=COTpmGw)U^qL{ur}w_HTHWLfq&PMGI$aj%GNNL&04O$3IC^q9`ssHT67y4p@BO_ zPb1AjKnF5YeoNr72C&cQSQx$d)QR6_Gj&_l(Z2HW;ogzgW@0aJVEmo)xTITZ%`Gsp zL2)Xj7gsI32jQ(*894c&`xkcK`r*eCaNCVbT03*CjQ;pVKxR}Ke70q6ryEQP-LHts z2fgCdE8ZYC$k${p&?$J9OMcSEauS!}cYJ&3$K0)TXY$=L=ofnx<(1rHO`;+T8Tnrc(WqPH*Z&0&ic z>cfZnm!sMU11BJ(k|CxlcL$yBqN!YFWye+_HR;BANFv$Ey}B1(eVQMILG;5#T3q@& zE=|LeIR9y$XlJ$2?Phz!2hIJnCwy|i%+-=gwTqi`c3Rycvej^3{Q`O#$KuFjL>R)J zShaP41&*cD*QGvz?S|T~D3G=dr}-ds8&c~zCIKRlILC`xBC8%c%SXDf`?6aTR0+t- zdi9b8x;ZwX0JXwaf9<@tgV=ruSO=jW6dU3_H z3^1AdLm22;)05-suWpcMwQrw|msjGt)5I5MLo*`HTEbixi8cJ56bkd)m0y;Un2`do z%~dxZb}BG|&d>!8`WVSa;)ZKy)#3o=n0wYr-g(*tp3X#e zvX9EZUv9e}e$hr*IkX9ul~wE}7o_xu{m`J0LI42Rjd7amgek3AR*=g;caz+jd^bR`3O zni2$4?U75(;9gm(3umwL?WN)hOSQ+!07?rS|-mkNCr=K=zY9d0E=qF^&#P$Ncp^@W$D*z{C~EAC2cS z^HwYPc_y4!gCCF@GDLXwNQ&IzA}VMyGjp5W>(72*4v@fFsw8x!G_TqBpc|X_tnpC-XN3VC!>3@XeZmNn1sPU}^ZugFb=+1Dl9Wu=L+to* z-)i1aq3#dj+Vl3Jcut$zI)s$vE~LH!-KgSgsiI*!KLUudNk0)+)@-t3Z}V6zD#>`zhiM zW2LUs1QTzXiqm$E#=on`-`mMhm`pl}oGDM&j2 zWCjN^QwroFJ)Sm&fYxtih~CMJqZ1Ig1p{f0+eKs8ZGwd%cMG1jy{sNU>1_r@)?pc~ z4mqhAj951APAgn`54KB*&71-Z_k8Zj5wo}ldJ6_#{6_WhX?``W z=axx~e=2~FrZmiT8L-aOqEax9ayfkTUq7o!KE28Z4!dG zK<6QDCkttBG{ikON!KdOlVLZycM_h+<`0=zL?c^CsC-#2xNajZx+w2+|mHKN_}1)h`eV`Nh2H@vdjzekqeY%ZHIiA>SA7Q_c)jgQ~5{z(PXLOoY7vH%Bcraz}NZUPX9i_~8N^#wrxjy;lwVC4F`;F!<__QgYlnPdc9v z%1}S@y5Xa~9ld}@y)R)hVye=i+6+iWnedtZ32UFkpR<5RIpK0A55&hS^P1kBIABcB zYZW;i^=ROf`{|cYUD;|WdRisM20n?2Qtrzb=Y~DgmJb;-2qn^#EqheShg6^LB&`Ci zZ+CP0JnDel#Jv}T8Wm5DNhcV1UPt_#z?f~plk z6oicexYM&(Btt@W43j8OVL`J}pdke^xw@uj)gsLNBFp&^V_&`2j-2c--mb8Hj85v2 zKO+xUm4CUvh3`@Mu^+`1lW2~=i={0~apur;nyei$0ke(;r@e#VJqgrY^)I3cc`XyXtIF~5O9sYv#iA4mVCmD^~Rob!` zz)K$UP#2GK?0=dq)Gu-5R-LvU0Y6TDBe0@Esq#cLd&lPHOz4IJ}%0|UITq=;Ik{Q_jaV~ zdpg2m)C;Q%57F6SczG1sjGKZedCxwELqeXQt9D0-tUsM+b~o~&ryWw>yL{hGTDEn_ zJ%RQDsNYT#9W2F-coi>M;#W7~y+55&?=vEao%GR~p2Og4!G_b41D#%o36c7n-iil~ zfQBj2@kuB7+Hlx4Z7L$zn-^dMroIm=O?_JKzUu%QuI)nj6^R5NcY7o_3lkcf&#{FD zv%Bv_uxb`b@2M#h(#dGs4V3hDL{L!fg-K19{&zaE${+>Qfv!lh#njuVIFo4s+2=l` z=@Vi{NEUbLzV2qQKANxRU4xR4;0mlNWu)V^^hn2BtI>^CUKRRlo+BTYpbeo;;JM2* z)`i}6YA5NvjA&~SR|s}_SQ96DLiMgy)N+A-2_I+smjS#0d3hin8CbftI%Gdjn6}BnYtdzo*_JC5_hiHm1@bk4CD?M*jSc02NI$d#(Jr-et7$6p63o;E4^ zQ$jZKm>+@yR-ey^tyk;SL)G%w)b`aHj`xjd_V+tXnYwvg4~B~kuKp<6@jYp}10Ov& zG+*S}d->HGU`rshwX=DGjB?F#Q@*_+NN+rUoZ;3GvL#4_Uo^sgdoQ29b2d&)ddyH! zE)t*Wa`RF_mMlorX}V!@EhNNDXqR?yd;h$NJlx3&>rX$v4`BS2!J}Tx`NfH2>!wef z*n}O&x%MX%gz4f3_by9i%C@x$X*Kj5xHg;=kJfr)@H9m_?3Oi}gky4VJdk=~l}<>} zlEm(-u1J=a5T;z=ZW!NsNu3a>Dru)3HFy+;&~#ZEKgH}}+PhS!d-4rTxV&)|04qLV z*q;fCm`{g0U%sRZUt5CNdOlvW0ar5LGFs@<^BwHT{O0IkS}(6Oj&xGeB?Lu^ZCB|T z6loS}J{}Pgv_AK$Zbm}jwIdgLDal?dm}%#_p+QdwtI#FSqauO%w3KnLxU=pjVuLWy z6T3U#KypqZLu`{$EBjYf8x@PLjOU(VLJ4l@p>#6C^x^)aH;`D`uN6yK2O}#!(`nop z6O(*g^H)pF^*Kx)tJ~?e7bzAT43_1AN=p6#)S$^ z`PWU2VNRjOTm9Q*6c+PdYY5lR+A;G3K4Kxd*g>$8Q<1EIqp)$ll|FeoE91nJ>9>SS zlm1H{#j9rihzmk9*JvsReTIoU-25>Jnn7#GV}rZ?QvP(4u%7n=^V+Tu4%(4S{C3_} zkQH0A1bE~=9Tlp<-(}!!sx62AI8!!Wh4Xp(mAk5kBS;7i4o)smFB(!17~eKsvky7r z$@y^gD4*FuTm##oBy~6dr$$qt*r55hK9TD^OOudx51uM_9S(7q4aIu|MS4ru5$y7; z*!?3y9TVU38Zv87XTetXMy*wrY-_ZO>^+16+U`STGq{y00|yE5dcjpk{Y)-DUv(LJ z7%$6RxfDC*X{^bZ_#4L7pPdCq63*jPDRBG8)ydCMm$2`4Fq7I2H7cQJG1DjUi6s_5 zaM_}5MwZVprY|m!e*KdP(z0XGgMZcflOo6x*fAm&Xkjj#x9o1$`(VI#bng^8jPXBZ zMxwAaBXoX7@}X~?qL`HlyTJN*Gg$m+^0I8*>^&2Wb5YI+p-v*`eoe~WK$B`>KQR|e z^-vtK#9>W=&4}T*1eF4w>;T9b3+63A1k|2B>Eo*e3O4aP>cTZIHRag!4GxtZlFxr3 zK`gn~*b_vROH=S(eQ3e^w#d9{6RQLuz|iIRSg-U2z!QX+of#-BzV223a_Zz9gWpXT z!W7}`c~a#`u`0__l7qUZCiMLcuL|6fqx8mgd-=KEO&%C?R4YJAmt1RiyqAhZQtmb9|I;D<-f>h<-0 za{RGNd$(9WF;m?@29!RcgEP?HwL98HMYfTAh}_S&jL!6ve}ZfY#$M9_^<#Kh4q$67 zxUuHf=ES~{SCaQU#yM8{YV}5W8j|))PfX4Ux29x;35*Nodn_4M9u5gLdZKDWQ+glW zS;y~X$Kghmvg{fS)iwpRZPl8R#-Yx_7e$+QZQ}Uon9?mm^urx!RhGUK z25Hk<7WPWMd}Pk6oPUN5q%{65k+ZY6N1Mv&8YDf5UM;kKh}3Od&r#WAlV>^6kiJiE zbZ~8V+}$xdiAGBwrKpWAL6Ur2-RZ{a;HVgO}iw*g@|pKUmVcblT@SypFWW^v`Y0=d{$_t~-N@>}+%rie}@2^1i)+ zi+NM-xh>lwG}e0;rxU*=?043n>0FWHxs=lGFD)I%c?mT_YH{CW5k|^s53}yBa|v+m zf=Z7tUd!bf{7+l+d=yRyx{{UCKAq2(#h|5nL0mRpN^23e=MBIk+oA@C_B40A9knZfI4O6r_Zf=5?xlNS5ch;H8%9ox@oRRr>|PVDfYkf(v}# zvi@!kF|#wxe&~;_R`p?n=Tcj~Qux-CxR^1_(Ywb+vxgj8Z`o>E`Yqq2Bm-~qWJ&FA zlP)S({+0dRyQXRfpO$dS1Xk9K$L(ua-pb*3=|v!hhU2;KuZftY`QyB+3`-qFG6oup z77ODf3F|0c+UP-wY+*>`SeG%<@C@@#XR5Z<^b1(&UmsAnb7j`jca=65*@h_J_s4m3 z2*|b#L$uFIM~TSI6WUY?fsi@9r&-jR)y_e(5Idz6lr+_7j1K~)X1{VsHq4gc+j2Q( z1s@@Sdv#-|9C3|Z>;59|<2$^_!(@F*t%S*!1N1nIc&UnJoTy2 zd4qge00Gm7-Fa89<2`DG<4!~r3o@p|+xWXbqyO_!^Rzyw?N#j+%%dOGaMiwJ6$`my z3n0_SY6rrrd3`R6G`V)2?}x-)J(a?$OoXC6pMhNoEbH zXh;BzKm0*cOFqiM#6DvN1W63G$h3nSd zT93+i(8-_hHZdE}e>d^po`N^Mj;>lM+W-cm9*GQ7y+pg6dLm)}6D~>iM2PU*{OWGo z5{UO?bWY=-$-8K2c%ClM&$otvT@qORQz9?rG*;lk~^F_%v__bhS_FAoc z|F~BlY(ORkww^zet9t~+ebl=&kSpJfTAJb+nO5@VJFHQU^K5M(nhxP5(WVkRntb+XXWJABiD_L+AJ z(VC!dw%R#AjU7AIJSjm_!F%~?oO)LJ6ctv%nwc6Al~b2^)1nqblsFUE*F%}yjp$jA z*sl@|GI!D?N9KqZ69@vwAhL7>*j2pXk<_(aUONshyIr@TkhEi=Lx=AVr7!LwNbKvf zssa!xDji~3NNKfVU9luhw7F#RH?(sIhnf8=ozgkJ3-Z=W$p>>E9%cC~ud#D#Utms) zETMaI@Oh#~Sf*gaY8m8+u(wDP{JJ-x%Uz$<;GQ1RvR!A}+}4)+v{S^{fm4ArB)-|@ zLcpb9@_Njo@9Wq=;`nNA{@)~ApsIRr%_xkn>c>wGOS=TO_@HH5-6 zMzm{At;XbR@`)!nr3_uA1rbYqmTnGSvU&az7r4`(^;8r)b??h1gp+)MGggO(dh_qjJRegTUN3uJv9mRVr8+pk`YVEK z=WCLWJ;5FrF=Q0i<8n2t zS&LU-_9Yl}uw2zN?Rwn~jP9t}J00$!P#U?jYkuy&eB?MTg%nJCZ{Tj4quA?eRIVDC zF8PobT5a0lq84)G?hrepzwfJUWJ89;L1&E1M7h%1dbT}AfI?+S0B*w7TDB(aJf*|n zy$l4$2lr@m2cEW9J+n?I$xE5-VB8D>dx#&ZXDhDq;a6r}4KVzgqIiZT| z(Pb3bY5ue2LhIT31!|n^uyE-WVo5N(qUlDkTSP77vXJwq>i0 z*p4t+N>6oi9cRr-(*;7|{h7vV-t4V?S*evpad)Dxj8;-k_lw8tWmT%33+4TAhOr=x8$!!@6IP zTdnzN@Gd_IbA6!H3~zl57O8 z@CABm^ls;0Cp5Gjob}4RX~C87jb0Ho*&V_hAqun4vA^3XN9y}8D7T|dGk-udhtl;>`pMgbxyRMT*z0G<_y){QneS|0Ni1dZK z+M9BxNwM!Ru`2soGlio5TKPa!uoC0sBWQ({#ai#Ca%psSw;9aMYxG(5&Bf3rC1u3E zyV}g=b%17f`<_jX)KQitvhe|%M^roznQCQ`iqnuj(h@9QgJt;9a_=HsglqE;@$h%;_ps6OqvU?6)J-rp*l`JYg>Enf+LhyTH zB>c@rOnj=O9oYo0d#~hBw;vIt9jt@Fs}!TKu5?MIDIfouR7;MBaJh_7kV7%_G!Eu3WaG z*sTh>?aHlB;MKA=CuJ;>Wie%TR-W?OC&y1)LaF)<7R=6+peZ%+$3MFQCjl-wnz-qa_|rmNi7_? z&4M0fT|hi90KI9jEp%UvApSC*@i^FlEYg-3;ErSZ1kFvttm+@0+tgGo<3NIGT7u_HSnk19jpZ7DpOOy<>AnisV^HA@`cZKWgFDJ$H@-m;}LG5YP6 zt9ySI2YIeMZ=hGJDF$|){S9F80$t9Wx)0Si{Vh^nnKs!Um~iV8R=6vzx&Tm++>S)E zmf*>vfzFK`ktsL$A+{>v@ZpEehLg!x4zM%_Dv$bSmftqAVpxw@MJ8Sv%}nW)sFGZT zxO4OpmpE7*Z)rSi)&Y}$6txK+vr!8wG<>TSS~!CL{=W33H0`KE-^V1V?ZCriA;<6f zrD3p)3BhUNK!@Es70!k))Plljh5Hvn!qz36Wg8jmdQOg*rXt7Byswnt%qS@Kv4a0U z_TDq9sc35#wt#|k5Kwv*MJZCH_a;rL(nX|q={1T{1f+=cCcP6nLIM#H=>!NJ0wTSJ z9!N;IoAZwQjqluhj%S?z_YWiNwM(+r+;h$H%x9Jh1)mGKTQNu<0LDep9IJiirnB8R zX>+65kL&!J6CZA-7VJ1IHGN@wU1CSOvY|`wU*WW;-l^QgP&l;hk`fv5oIKo1RN7}_ zU$m=gqS(66U!2UDu<=E3SQ8+R;=_iu-hB)4j;>aH1}aR!KAJ6O$8zVx71iK(g17+C zfS$npGi7-38}p{~kmm@CfE-_>bGwTTsr z&ofug3o0Uaf`E!LShwi!iew#xvzw2GAF`7pEN{1NPPPhF^z}+3nRF{E4}er{XU74l z%~|$3q*o&?*X-ECouRnd21_r-4_UPiNiJfYT3a}5V}SUy5%{TRnGt^zc7&{bGgRn} z)jGDPX@)i?U|gkfGcN)vnr1R6P1AJi&+~g}RUxD7kmJx%r_ZK~6Cw7Fgk~XYzv^j4 z7W=*y8zN|?CnP&w$+)Qoiwd%i33=(HhjbG%=LY;OCn#S>td)s}EUfs)?mD6A<3rAv z`@?DOYG$tdh*R_3IL>qLJ=y^I*ZWL4Grk-nxdhkXH=8yKs}>F8L-1$tk9cfzxiFC(zWJ+nrZPlk1DjxJQ|dL{YGtut4EF>(sy%g zV`KLf;+OVD^&$K-Hy+q*CPg~F{r;-*?CZ4Ki+p9z08Cl;N=^{#ZRWZ;F*29Ucrmi4 zv|Oi>xiV_t(^#5KRXKM)uC2S@^sQQ{sOe+=)2uNVYn+OR&RLN5hz*vrXfsF=tGiJwo8!rC>J8*5LsmJNm% zMaUwYcd@cD{WOnzBOkkL%GQbNFJl_85&F)O~>?;pV~>ag6j(r;X77UB_kR|kvGg;8o}IjChq*Lkwp=Ah4n`aP(C61D=DctMk`RD zA3O945=h_$s=A#Dl%OuYSvHVvS9&CVd1@m0$am@2r%`0NM$)g1YW;Ter#05&<)DIU zn2+-e?Noyt3WiQOm$c45Kj?oUQ0n+lc~i&7wBm*A>ubLxxdPqXCApd+XfGv|p)lt? zqr-su6}{$>?|Q!C%q%8Kk&@dDV$9TL?-QEFE1wx`8t~h}?A8&?#9w5S#oyQID6aQe;GGP%jRK)*c2$)_p0Xq2L(Yo;X2{NhmK zA#SS>_vEl=>e{L4px%2hra)T>Y?T&LUP~t3F5&c%^}|?nQqvy`RbFU-l+39bH;sJ5 zEWdfnxW#QT8BiD91w;z^ZRn9n!`pSrb(*#C2)PU28Cac--~Hv%{>H!}Mh`@QTW(|P zZx9Es>ZW9*Vm*qRjK3Ag005A&`hHj29tU=HpZcxWN6d5;jS=fQuKFhJY0%NxqnO@` z!j!UKrQ#K?rB zYj=jczol!n`(oJHa{4^qn`Hx=knRw!p{`vKQtyf2llS!8j~< zy@SDT-3%#f)&83u4b5gb?^Nc;foJmxzS8k)oeR!}mi!USyIOMy$zH;30~j~FWy1^A zP+V2;li&}{er>7?Ai1a2EvtP`nD{&)!$Fpvu}#!Y4fdToiDU{+QBOFxAhULMm`N)h zO!777bf@A#=KMyRco|a{O1((mW* z;HyZc=6-s5xcw8LvfFt*rduU4yYfM2KEAqeoE^k;Tn|OhN8)RyB_D+d5wN>PsXvr! zs$ylSZXRc|DZ6s_LyAQnXV4?E*tP?UbyS3a_7#nX6f?d$ky905$25&jO<(nGcWpvN zGCP$f=6iF6)_D=*nm}l?t4JRI1*Yq5QS}|L^*BQpCsHXROvtQfPqo-f)*pX{s)<{{I%bJFf- zAA}t)kMVu-=Oi*UDg2>Q$0`*~O225y#Bs#1FGaES0%Sg8Wsuh_|0^l1;JtWA;oS_A zNaRHE)n8i!o*WNlH$A#&@TB2PK5d*70u`**+9++t4@{1Vtx|p^AH3mGyvJ?R#i1rJ z_(IxZ1CRr49DS)mbH(jN;|FENUFk|rjgu~tCmGIPJ5)qRP=Qr036F)g9Tpz@BEKRU zJ6y1`B&IwpheM($Cctp0_pXOZY_fuycLpRj1bb@5WG?*FBFh=JaD|;hYV+}}aK~Q` zsl;Kd*7F*e*p@1b_d=GmdQ~Ui{EvN6YN^gD&u4kiETP!7FT8zZ<#gz5R(i@6(#Ohx$!#L73$R>#j4-~?MtIGJpF%D%vT8xn&dGb zze)S!8BRvOrr}2Pz1KDxTt|3SW>#uLSK+xkTCNJ+JYt`b^=H#J_O@XWT5?U z`YaSh5fW;m4{9f`uVSz5X<9gch2BbBz|>1M$c^UeIR9Y2fkw;OMc*Jq7F&x{6{f{% zcUqcWeKS@%f)8+fVoS$h8S{#@hA7quVeF7Q_oX;TnCcLvQRSZVs*YGhNb zO?Qk@G?|mvD*gfNLRMeQU8k&vt|2G8B(K|PTrqjs8;F769O%PCLeExuYEZkWt^hgd z`58&uaFZEfonBG&n+jm>;-Rc0xR;$M6Sh>AuySwC6KH}|{nZS7R3;kVIF`n@aly~_fI$=XjO*%5d!69j( zmG)r|#1*fqxvd(&>#B!MTdt^8;a|)W{BZF?l~vvI7W@5PZifS<52R?^2gzp_%Bu8@ zF^KZ`6O7a80C{pXZhKhJ$K!x$&2mC;qQ*+E{KE`6)aIHK8Y*mtAx*!Rz016Gb7uPh zdf?dE4dCE*vp+{%6}@uzZdk)w1?s*9Pw6x|kw50xg02D9uqrAHZ(~%KyW}%{{Hp_PLCI3=QjNb3A`N@YTx@cjsjg87+67VF@@T4mciX z*ZbyS4a(M)JsV)mpBQ?w@`YySbw=)Zc?kh`3ctlYcj#H1`A9`c*}Z_Y?U_bRmXITj zk+a?jfK7zMKF6nhVQ02^j%$s>cSY6-L6+U*4;1FNp%=L8k~D+^!kbMnE!qjADr#}9*D-X1 zQ0pI}4yAH%xr_X{z%6aPnwL*uBC%5?aGy|d)^8VXy*!P+Iyx%1ISY&=x%R1w^Cxrre<;;5tk)_%v-h_0ZPSbc11N{|#Y&2Zv}&Qe`k_JI z3|{LZ=wdcVv_GXc99PMLL${iDuL~qJ%llK>lw z-HVL_OTr=IV#%DL`kkLC$>(p2jA{`!GUzTv_k9XsqyB-eOqEE^g&Ln`Ka1^znO4Q- z0=@bEFe~h-$Cx*fa-UJ-pJpww{ zY&iDS#&nIG9l4ERPX=dQ=74z9L;=n6 zQB!hJR@wQJK?8GK^x*ja<^pI;R`VeYiOjl7c@5=MK89Z<5caWXR;&s|xkL=-G`_Ca z!Dugp^iCX|Nu&3P9euI4X)JgBOxG7+@!swn^c3fVLE;Wy@h(e2O~m&m#m3*QWp38k z*n!D{N-XA!d^50dw$?z3ua@b(&);fnfQ=_b{%P2~UbyCU3*S3Hrrzwd6YEs^{QXNe zO1%;=3RD65Wl9LIUV{((`Uv!7?Ng#aJ`|t#hk(A&TLr5em4p*V(LOSGhQ|jbvVLiJ zhzyx@)oTw9z>YKi!Sr6p@){qt|H>?(Wrs^|$&EH@@;orBt~{3vef1)_hXpB+0uWK3 z&6bFcoJOgJu0NgB?{tUZ6rN^mdLFI{s5>Lf+q8Ck*5`qGfUd1I#P!_6Yvz1rVrVFj zHlFX%lfd)+?$ymh4X{_DwR-c6qfR(mBYb557XZW@1C;J1`f? z@faL8?whKGMU&qISFNoWYuu!f97kU^we37%Z0H1V)Lj&c_#=eFv};)W&fv+++GU4Z zK;V~?S~KL0HoqoW|=W{p8gLTe6thFpe zM=01yez@w_-D&%(uv2gF>N_QMwd$=<)O=Yny>x?uF|*qX5Qo{|Efsx3EaL5Ew2zeS z>FF?N_$%$*$aoTM$Shj4(~i})e78lrd0c)h=w^kGQ;pa1*q(erH3(rsw!CbVJg41R zd^{gZs<^q=J7Nl_aq7kJL2%aN$IzV?$gIP72Tx!`^Ke#fHC)_qDI>2b~*kC~bp5bRaoUqX? zUgf~sFodQ6Z;5>>y%GQ=M$WKRSHH+tzdG{FQ>ck{%ddFaReYctj>r^mNY=ceO;^QM zfvVT#sV17UT%hOM`YBMElk8{y@@D$q84wcIF0n3pN(o}yr3&iJ(`Sx@2%oSCulF{) zCnni$0KyxJ>7#5ptj7KTxWs?@yhbd(%?;hG{l4jS*>Ipkfb=t7{6kna!|Tzv2Corc z^%mNVRfgg)ZR&{}Z~FPfdw@{+HrVFjuMN_wP2aN>ndOa=+~ZW9er15D@b$#DL@4(l zxE-&0#8U_K>eY#OUywMvT8whL1N!I-N){9Q^Kfw}9vHCc+!Xdovz!P`J1?TF|94fb@obBMR0dUSr z_}a}rGV%FUX8HH|rq9LdHl&t5cC!w7#X&ddj6?nBLCcbLX zkjl5Y5z#H8Lwvji)fp0O@MW$#5#Ou-@;hWQrPkQ$;qp=2S7Fz9YdM7JD%i_|$Pz14 z1@C!Q0;$G0Hnz4JMt{6<(U9@V6maZXCpo;CP5T+;@Jr#>kxye=KUv;v!<*tpkVCtu z2`>uqaR!nMr({j&+oWu8E<+CVeKSAVyg#j7#rbZ_#7NU1 z*`aIH$c=I(RB@N(GSr|F=0cM3(LmAg`nm; zkzM4ip_pp_Vn33*LI>cQklFW4&b&pI_Ph2~4h&f?p5ixq|t{q z&{{NxIT>XJ0W^QcE!|_hJ9iy-J%#E*Ukf%mwQ$@coz}@Yd!_IqTpJ=l_Q5j9G~~H` zv+Z$K9VX5T`$Ob!?tDV!wG3OY7+lNR2*gi!get*Rvx4a5vwK5ssj0GuT9iVjX_^A3 zr=A5+XWxNZ3!8S|K*Vcf=b46D(jYY1S>ST>!H8P!X@_)>G?{R={~zKvCh?kurj6yk zzU6Q+SJQyS`&D~rEGjShi7z)#7^qjW0DNskc~#g@E?&YW0WNk&m!Bj2AJV4I*X4^uJfACxSChWqNJBF6f_2NiB@}9dL5M;$W)AoK% zu|R`{Ymd_Jzf$a#eBH)KI#K?Sp)a<`ab@zzbQp(ha&;CsKL;?K5rzVdSV`dZD1-#q zk9kMjZSLQ;9}YbZIRaP{1Cs%%taQb&wB{RAv@Cxn=?qKQ#VY3RtFZ>i14VEx#w+@f zyS`?F8KTM$6)Zyo{7|is{?FDkH|Rw@3YE;7%Zw!r+4Qcf&NM_9&I0I*8>!pvb3S!m zrWIs5EdGjGk;C#PfwSwOsJ1<#mZkHEy5Joh6Z`U-6*!MxoB8vyXXPHP$&8wGdvWWVR)dUhhD5V$9E>`+& zX;&`dpKc4IvcSu6zTlF<@x?vZXg5j zs%&JrRlnGdVB3Kx%k2awc3H|Dw@;$Khyj89`bD+ui4}%BpMtG6>bBj|Tj=gis{(-W z0z;+aoVgbJT>8E$yIqYCol$4b-OaRl-UCEIcM%{L50#}-8|DptbJGKIx0+0~#Vb&H zWMS>YB}McZmH-&kWu~c{B*N&S)lU+US=rXy{U;@r(51Qd%VO+*pC{m|mp`Jk+0-&S z@<}pbTM7|}$);2w$0?Ye>_uZ`auD_OA0qUeUo=0J_RCxhna+F5DL!0NVD@~JU)TGY zOMeRA0k|xxISB3cz__W>p!At#KH(KtR!%w5w+(^EPSefqxtBz86?1pCWI51f3yq`8 zK<^E$J5^c4%^#5|n(W2g^JQ(@WDd8gvRP!F}BFCL4 z)XP2>73yT0Qr~^04R)XQxwy$}oNFwT6Y<`s=Qzs&VEz96CsHW#3*v9hAK$KIR5x)x<0~>h2c_OY7v~L@5 zzyBizk{*G}b6CgwTn$6y)`Y= zP2KVfQ!|RzC7|qyeH*d{Brk95AdJe!Z6%(cJucN9_)lCh?>yc)DD><#)}b;Zcnfw*OPh1c+N|Ei%jT9N7pP1BSHv1m^gUJ3#Awk2fLgu&$a;dFrQ+owNoNt%= zF2yaKY8b7ZiLtc_%-!4VjTM>w|5$hajUdVkkubecP;Yy|5HVI$K&CWP_mv^Ep}&n7 z|38fRY|(F->7@Ez*=`doO9&DPblaIt)5+yKL2&! zjsGW~|7Ko(lH685XIEJjFiob3TuJ*UrSMPc=3nZjgl>XTqV#V5`z8O8W#*sm{5_8= zfs>Z~Vame?;kQ*>Nv6yKa?*1E5?_76`iARMDSOWkLa*gvi5r9OsMH&-z+dEdNzCUx z`hMtp-zsECX5{jbbZh>RbywiCdBMjLXl0T;D7R_W5_2Cg6nQYdHb=wv{V>JBGtTcm zFvQ$voa?0v+{o---3Z>kM9XI`i1p1O2(ok7=K7}Y{oisihFx+oLImmml{Wp$vCr~A zSsJxDSLtB^M(xKefewtJq%xbXOhx11kSgz5UxC z_>Sw67L|bhBmdv&4gRkmC|?86dA+1x{ckU$zNA78i&3WkFLz3|7r;8yka+#Kml0o5 zp?0X`$NbZK^q&`n^3gLkf3g4iabS{46Nn|w;5`jYlaZjjs8rR3Ee&o^)T@->+^@HM?8In$<}Dhp*HtPy@QSD&g^4{vemg z{;fPe-EZ_r%jutCQ3A>+jj!@r&hf2~WsO7KSUB|N8X9@Q&*ajyB`+1IxKRSnanXTMU@N#6f)*F%fopzsPTekdt5 zIZ5$%0v#JsfVx$v4nWBFrs6xmHNFo3)wl7-vB;=SR9WySMM!6s@l%8>xu(km z=}a|+fLorD%Rv>Uq}%_+RR8}aHvw@?E)JtHAAHCRV+2Dnts;{7c@3_JnOyq*pB8=# z0zl_h;@M@Gt~4(p33WP=(xT=lQK6qMJRMsC#bYw9*AUgNi~`Rpg_m&H^_uJZmp$`( z*stP^;OwW$%tlHEv(4@lQszK-g3C%)RfR%nk>py7*2|Inu27;;U)R;j={nyX)3RrI zVcE%xa2QY{Tdb8%*Mv{eY>o(7Bl!<2)}rw8{FJz?_3>(|XF9p!CXq|x&i8k?#QM`X z`G0&*_h~5iI4V-}%l*Y%D=3I{2bD$)tPk5wKzpyUg>sN14-YEz2-(#_ap?dj_#>*w zwL5_4sqqeTycH9XlFDzfidu}~WU%EW*g(C#y!3DS@U#5y4>Xd!Q?*N-fq_RO%$@Cr zzaS>(roh$lIHht2~twUeA?oX*zcE1h*W-H^0?bZ6#)UD>Y${XohQd&2u zoByEn04z6nv&gC~RDXnIz$8k*WkDfhH_CRx_YsTxxQKQ7;3c zxh)8-adxl*4N2iO>_Y-XO?ZH=Zly2T`tTM#ze~gqKGfiBi&rYp(P(Pc9%x)z1jIS? zfOKxn@lNU`h1t97H@7Ei-~hbsRLRD;%Ct)kUXy$p26$)5Yc_|gg8*8`$lm=`PUL}J zp}O^St53y1rnr@u+Z-40kfe#K_ZK5y`;F7r9CuoMKBH=a=l6yKQ7Mm1I9y9`h-r4vuDUR(GXR1)?ikB)D5q12-zwnnfzP$A#mDV#8Nx!off&&yRYUNQ@7I3^Y7l3$-%dM>xa^a zP!xBMLSqL@RdyxUQPUhM(f$$*Jw9jrpZCR|@gqjR!q;*^4*;IKvVbx--EpaqGXMnP zI9h<^c5HW88_MPZx1Y3XWr}gSuwHg=G;?cF(2=Z-Iog@D0xXU@P3K!+S>kiGi3O0r z=fzI79`S`x)+V6E?{=+Pe;2(K+hC?>WBoatS*oeO2pM#?;PYp_K&p=>UhYwDFs221 zGQ(IlbPOoaUZfv#l`v;J0+a#LvP3k)a2cER@bO8^As#)W`Q~(y>>Q=_^WNSMlJ_g& z9ZJg)_{n}Uabyz;=ukVD4`PjA4*ZSK?WsmW zmTS9nZb3>wcbV~XS{X-~v8T(zBk->iWeCqV9iXA4)xk_czLX$T(6d#z)t#p8PkG=p zK_g^M0K|7k=XiVLmK!MSuxa;#ljz(ja%O3_6W#q;RAN)C_^UfCC#^8h8d(m>S0Q`iR z=_Ga?pp>cpWFB&yJX*TASjKOoBkSHIi~%_X9}c1q_M>!dYtL9giB*^6C{5ZA59RI$ z-oVEz_~D3YczU~6QD*=lTT4A|!)r)ZAK-(TB2asHcF4h#lIF3u7q8lrY84l2z8o^S zR&LhpRX7{4ri|;(zflgtI5t25V0WGOM%{PZ`ob16$ovuWK#+9R#WN0%CG#gqYkc0% z{pa=EbaI++$N1~+L-(O7^ZL^6i5&abp~GK?bOGYPk?5Z&DL5nCY#I6UknRCB3?=Oj zoZ?3VH!D;>Z$JFUMcV&!QD~{O`$#BCIOxQsWn}{LL;l!r03fJ&HU;&^gCdTB0*l(tEuXmm{X?$FOeb zdj7>JVffr(9l|7FZ7QVjxa^$@nYdHhb2G4|%%~|6s8ESu+Cj(eat>Z+GVeL0!=ZmF zplzTaca|Mg!qw|6n#Wn~C!3n1HP+{K1{oSb7w;E^0uH`>H&YRIlM&eB2dbc7mA{f# z0Kqr0Hs6)dgu7eMa?5P0ntZ4L!OI)Fc;jK*^W!Mv*bG{wnS7s@@F23B&(6*dD2n_d zYizYf$jMRBHjQ(9qGzQ}qy4=yIiT0EPP`5WfSFs4n;_7mkYynxD(&&o0GW}p9Xy8< z2jjDc_N#o_gN7>8MoB@+6~5ThrGa`KG=A$t)O7IRS9vsxOq(c= zcFuN)@A@$Nd$Z!oV$d`)U=mrpIs0Y=_nP}zSrD>D;x;M2nPgLW=>wTt+Fe^`X_7vz z16gaxqmLM~ z?{c*YJ!>>(-oKcZqIgpAkw4mMLmfOCl*n;SuZW&XrE-cfI_zzfL1MlE)w8QAD(@(i zuM&2|5tY6rx_0Y+*wK6t>bE$L^TB5*v?;S0+t+fQqIFL=X*qX}OTg<*ca~o>#`AT2 zVRUI#?)`U3$fF&Fc6~G3FO*d!n5*{j{vQucC2x=lSU|bN%W!1lS=BeAOwno7A?aj_ z_4JZ{G;`-!q}$E+--(eigV|6cN}w4;}-rodTm7y;;YRaanx?KG5NN6JYEUX zGc|XQqc60C;5)Y_ow3z0t&qUz=;{5u%s`Wd=eXe`+b1_bxZ~#F!yT70D{x$(WsRz@ zG$4kzJ}zjtpKqECf+VP5 zSP2zSJ&mV(4qg>hpR0W&E=7f2-MAc>R{r~dtmXM&*}~1+R2Dlo$5Ad~M>o^Dg5BC7 zI!>(On1j{9i(Y7p-g82VG|@v%R}s;}uoS=2_KmrgK@(YoP6*4oaSh3X>&K3-0#*mo znYws%rSVZ#wJGpVF?ocw>+YCE4JTN#z8uRDW68Kf)Y31UuGOcU9;eWp7rX;T>+-2%YwiRD_H;p6}i>a#)|1V+$?4vL-gZ++^n@m_F?Xt9pA2IYjSG&Z(upobhm)G-kmK`pMNI z&enZlEeIKc?pqA}5b*qTT{xUgvONy0FRS{NY5$jcfc*eAv{7LA8=pn17Ua_3?h(qj zX0!Sg=#x!38sKHy!NoP(AfcfD2dtN_*M;K~OR;EwV;GmMKFr5$XBHa~rbT*3@VA-Q z$j=I@!745r%=dTipB^EQn(`~oG?Zl37Lc{J4^O2Gl}n5*nU^av66JiipMV__ektl- z6E9!~lZmgK>guEkYP1{X$H+&-?0$a-Cgi>6-Y#lNY{!4|QTMf7YI!boB;{S|{9n(s zjL)}x26i?A9z6D$mD|Q%@l4X^GH#oLphCaqw+)TjU)P}`^V}TMy`JI)`JTYuc}+}0 z#g(4rb*Hueo%LDww{()zR@fMN0jhHqNIH%)<$N&IoDCXyf%80Z0iVf+V6e2ro3x+% z7d$97M-FXGihgw8w7K)N-4zmdO`#si_JRC_;jOa<*b?`;-U;q<+P8Z=xgcnNrd_}s z6-^a4=h7Jg+n_a>fX?Np64)!(#`+e(Oyb#aeD7}J1_Ld>iK>C>KNrGA7AoyOK)jB` zAL?QlfdSQTQoz8$e;(NX)~7M&9rDhrAz^*aOs z*}Lbm=95gIS!#{`b$4R5PAj?EGyfK(yPKO-%V@n_2=_vWIM4dHHNo!=h4p7)Zi3__ zs@aqWF#<5(+s^=Gmxkta#dhf#T$VWQ1t6dUwYh9V$YXRj>qFbsM=18+p0YNj=$pGRV-=sl-9|7D zOOXOJ?wstoP&|u7*6>ltdBdo6d{a|+IqH!1wKAF@&8WSY)}WJJpW9SRCQDsIa82w9 zT#_YMS1!|fnhxu$+$oJ4#NHoSkk(qiRBnaq?(??+HFxhwoPtDL??~J|8u&zm$&3tV ze1ORmR{=f|;|4o_e7cPr%;W$*eZa3;FRB9SQ9BX%qzF2!@!+J5$J5;sZ=SbMQ)KnK zkKHlw9e}hG#^s%yNj0)k1Y8QqvDul_&2604Y8~WDDCeDeiX=v|H4zNL`>^}Bl4$U^ z=^p`ukHI{j5l7O z0_c0r;29fnA#t6#^6mHE7axj>6d~t97p@Pib_4map0w=@R9^*|il+xU4Ou1L7^Lu- zSzA^Xu^TY~o!gaO?l0+*ghl^)qE^+>!jx?+!-)~YVEto9 z3F$EneQ9Ycm|M7J>X8_svSQ_Y)5br$l%(&Xz^-C8;A8fK3q1@Xu zS2vE;Y=~o?>0@@9BD?eBcPCE}I>0b<@}iSs$Sgx_!fvOtmy?ELU^0?i=ttth!lq?F zbqG%k(a~^Tr>VoCV)(7iLDiKncsrR$U=MEgkQBmw!bcp&pqo7c(@R_db@mCrx*zHB zj~yCg_N{FIAqyN*1s$F$ebYY@2TtIdvs`tQ%Q+vcpX?}_3-n{&m37xtC^6=f?BRsal&E!Tgha%MiRA{?YZDWR3Jo(2w$!XPKEeqkCGab@K1d z4}L}EeU<}-idG)v&eSwVncFuPm&3}`(EKxk({~P<79L*TQF%S-7XMNxV`#&!StL;> zd&W8B%Q}6hdi2Tjr3BFKoJ4Qh3)md7?Zp;Ilpwv|NSc1((LNHn(wPF@UN8ErHP{AE9+(8vp9+fa@?)51cJE(PrsknQv z-+htf*Bz_Z85yXYfZ<zA<$%*q`XP z9pU2wH+;m-r`J?mO$)9U!|78cHuHDO%4Qe6C^Z0rkEbL!92WI(MlTdSTI@?wczP5U_ zCGPS?*j_p@WoxXAk2O?K^`5qYlBaGiM?DOyXt_JySz2--C@f@fjiMq`Jjn927zmXS zbKdO#MzN4orhl(D)k-ThzzZhIVmatj@K_sE&msz_O<))9{80s{&8MI7tBVSkit-^~ zL;_oKfdBA<^rN|_z}CzK=w6mf-P;W4-iI3h<}z9ee99dow>Aj5A7C`jKkh^LOj_{V z6?&bJuM$(5m_beO{NuEH5OWYHtbMKm4%6TaI9s`I75LiG^Sax;wZ(J0;>}Gg^DV%# zN>;x-&IR=XRNAq&t~qXeHVYs%*&JyFyHHu}(6qxw5o3iAhmV|;hE(jeF_cWdW2nwG ztVcMsIv(#4_5wW2uVbir*!uF3__o0Do;SDHqZjk{Deao2Oy^cTwZEis$#6HTc9M@jyTg6}*Q*Kbjfv_K_Th5jBnGu4gFLS?*ilbDL#Z1x6L?1`_ zeEYaTx51(AQj&?!Lp6s4PGRM%jPE1#mB)wiE`*1=NHG`yD!e&>ffwry}$+0ZNcQgdM1mSJnE@G(1^ zl!}dANcCsGP?4r)*(f)mRobs|V3Su%0ru#Bh@P+t=x`Viy%+xwJuP5Nis zN(yaY4GAwLb`X$QrKEpzSywBJJ0h78Ro#i~{uC8(%!H|=u7|a6rV+jlOwV+$UYS~p z4^4GM6mNAsIaXjZIv=bGeaM;y#n^bxv*%|{Q?3`NQ*k>yHhGUZe%%qKQp_}>N-!>* zzmgT<&Uqzz`pw!<(Q)>)TC0UbbLVolY)9k74!-CSUO{HidPRqnZk=H}DsI@^Z!)zNa;-FM9n8a$NAw;w}S z>8(QffY5a_smk3)aW+mwIuM27->-@6x8amIQ@yHjf!z;gAESWYt8uTW+B{YVh&!FP z4%6)5DdW=p-XAFU{OY4`P;m*iZm_nK$KdjG1^kwKsAL?qJQw|_Zd>&b&V8G{ zA43o^rOn@&>a1gw@BbL=@&WIQh+~%99cN-GMHC<7%daH9?*H;&hx_ZH6I-9qtB!?W zcHj;Lk9G}x*$WXZFWtl?&Ue0co>_cTXFs&Nv+<`Zh8Ul<-7`{PIepr3ds~KFp^}Y_xVXz47hHPQaz4b*C0{wgVQFBxKJCSThD+47GChj|D>y8)YtxOy0XG!Cz~>-}T6GLSB88L%3=yeCeC*-1(B+FkybbYiENA?HM`M<-yZ zHP0;kG$w+fG!bb3+xrkq{yC9zVT40Fvmd=ZqO#jwQ6$m-n5_12tSna}(>^E3=c3Z@ z5zS8ek@zvG21r=P29x=Y;}P-YN(a9hSr`zR(?u+-yRX(qNL-i< z4VdGY%(KdTSi0l4++si+2JC5ZgIb+9VDm83yU%@$Y}C&+FP&Wi$4g1%n@SwF+4gQC zix_x|Yk>~j4>WRR+Xk|w7RsVh*0_cQFP=O$RzA|O+EQw%lw~P)D}B*1 z7rWpcNIM-zXGU<8IWF~-;B2;T<=4=7xrvgbPoV58m4vBh;<(PKeKr~sX61MttM>Il zW{E${GDD8oKC<8t&Ar+vYGf6fTaOk=RD~KJBFQaGPW3zB_V;Va{*J% zgI4biu5sr%E}%@`7!ZzEkG!7#-pW4Nu}T2LGT7a)Z3xU~sd)VAIW^y~r-x)S&Y~WN z1eTj+=BT;ny!4mXhsp=@3zgFTKvY0|Lt=ixAB2D_$m(u#{SYq#R{W?w249>+wD1KG z3j~}Wlc~cItPjYJwj5;LUGn3ew#~>wmV-t6HZs*#Aa%@dvUa$8=ZG9X^&CYt{+%56 z9*;TS36>Br1&M`Wm2E-I`ng^ln5xfM>8KSi$ri9g)SLrgj;+qrH}oX7=c_ci;nGHV z0zQ0BHsn$lFHp!3zn6i!bRg3M)*YL%wAJQq<>IdCmf!q4git4>KNBWXA(nQ77`;$@ z!1M3ekv%GRjUjW1LSMcGoV-peeKTM(%*ODtb19SRP~6Y9TEBn1HJLcq;^o|JUC&TG znoB7Jp*Eay9p{A;4nSRS5oD_-;0AqVNg|={*t;8sDR=i(u82Q8w^rX?=}*=C#2~sH zPA{nu<`)4YE2?HtsC?mcCxNU6k62VRozPy6K`_AxO(U^ zyrG@htbUo1?c;n?h0OYqJKO(32z47rRXLO+Q_}9x@8GxzL_zC9ajKe@m4nI^uro<= zDc@}mCfPvb33O%Bkx|?=C7gs_LN&#*=4GM^Q5m?vEjyDCv{$k->qnF$ zI#GExyGAboyQqDdA>vkQ9AEYT$9L4ZvCR3g4JOg30xE!ohZ+@Yk`#K#anW|ELOoe{f2paQL2URutf^Y!dx zi+8UOOi_KiA6RQ{2@b*8X1>w2f~Q0+KsR=QoNs$`LEvL$WUWn!v4qzR9YM&Vt;{c5 zpqikymgv?@Z&JyfL!FOWyl-xQh+n;_SIFLQf1@UckOY)SvIZx~Z{a?-)%z3wCoU0| zHL<2T53C2lthOf0wGp;Zkt7d<+`!I_B;SL(%0Z^>fBe8P(q9gA?BbYidLI4yl*!i> z`fTLY&Lru56pf9r-N0wS6251bxC(geuQ5mImV>kcMgtEio?lFWtprq}Z%H~9 z4lv`n zPYJJ8jfmL0UoKvMew1GC(D{=7(F=LXDxa;1oogLO>B9Dnj`s|Ut=|WJ*8EuHJXU%S zNQfnzi~#R%gxO4&%=z9T=jKwh`ApmS>HgB2levHnk0VNkg8TF41#y7%^8 zmiPLU35{9?BW4RMYN@EMd__7xzkbHbDSc~^q0U6+)DjeOxBI5C%j`Z4&(96hPriHS z?*za{@k#@(knCa@q3)#~Bf-0P@@s&+R{wm_1O!_mn?7%OWFf5NQpyQIO;1t_VG>@R zeV6JlYbfT~!Mb|~;?hbAlQYZG99_t2d9pGJs8F3XmHPoBtL}G&CDDWVsHyXzJGgJc z*N@B=Wjyp2EYzN|`hE5LgCF_`xM}`;mPHXQKAR=EMHq`qQv{fQBbeczkKEOEk8%+Y zd)eGuhx3cF^ky5CiQ2mFQ#x&

    Nhw0)MB{hx@$eEs$eqSB|b@M0g!`U{$*&Y(a*wFeG7&WwGd#YGv# z%U-B|$D`D1@J2`H&yPuiCYBojOvZnOy)PCRN}M!ca)7B32T04WeinCU;bgtW4f%b|W(I^MQ`Vnw@KY_~x;NY|6TPY{$M;OJ z24dKMCs5Rodh+{bt?GBz3!-q{p)3iJ$3n`zc8tXz^xfk_^=U{sSbwbe{ersl=NAFc z1UD+pMQhLA&kK)Mp8YTO-a06*w@Cv{fFzLM5D1VUgNNW2+}$BSkO0Bm9frYzy9Rf6 z3qFwG9^BpC8FaXZZ+GwA+P}X2)%|m;rlzPfoSAcGy8C_m>F4Q&EyDOQujz5mbUknC zMuMypLeN>8CzzT6U}iGz&efGCt5fRX73%33*Owoz{g!~{O}trt2V@@9B;pq3+q-+a zKVbf8@NkU^B}@Io+s3103(@9)*$SSmGg%2LA3!at_8NDPvFxZ>0;dD$O*E3|&I4dW zoT^TP7;(G#ZIdUb-jmHEbSxKjLdk`gYn@3o&@*fyeV)*_bj4N-!~4ambOZhICE-JX z@7&|KD|9`pV!PVx$|11D_c`ye(Iz_9+MhqLkr@==H)$q(Hx3J#@)YTMEe;*WMEqKa z!6#tUUvQSwWRiS1O(|n^T}js87UxGhQ0V`2cPx(`e{_sXHk$ji)OUXd=724Hlt`;0 zeR~O>ZOGq^KP7!!ZFn@B#zak;E1M5ht~eT+(+`r=CE^y|DhafEcmEi#u9M*V;)57n_+n>aNTOnEDV%<2VymVscqtY zAIJpEeaGgz17k(NQ=RKJB~ibc)rpWna9=mh+t@PalJn_soxtKu0!NgDfmo=X#iZYm)-Oq$%Yo4C1$-Zag9R?r!hUAO z{?Y0ct@1mc*p|DJ!_9Rg{$l^At`KTu%E4FLIg$L)p?d>AWAdc?i((55&w5necLXHw zlNL_^Y>w=Hd3F`QV~N zhp#|qhXUA_)v)>|bp`WrAe-0Px+D;_bPSm*WW+EE`95+ISm<)?vu7AYs0EOx2D0s> z4{`xiOE2d2z#=eDLuA5`_P`+DXkZ>~G`3N4*$Z*vWCleSZ|=w6EqhrB4jp-kcYFni;r> z!ROj0h8)a`j0|}+$iwS9-HR{Loa_8*_4KRsH08mfuCBry>8X4o| zMG*VxQzfCKO32wqBPO0?X*@0ydxQ7)G6R>-5Qg*R zh&+NT!{0J}CliF6uRPf}q|L69D8UR3Sp91>7`vL{MDpZ@R2i6A^2)T0u z7TsOaV!#VNOTn)Yk+51%h!^gU-d_UR|J$NpUU%xgW*J7=iQWKE+$TjgHL@GmvPH3N z7gp~?8Q4a-)mSTfx)u?L0B*jPK`+s&|CNY|4Q^McH>2j9F3&TQohrYR0NSi<)o~#c z=Mt5pT}6jfp}NQFwBDL^QGG0XPwyl60Zpa|bR;@eCC4Sp-MmRYB_n!GgyG7AI zHow1AR$~;Tb>HAXxaN9O-Hv5?$-&&E&e|Z(MNlqajAfX)|5Jr%FccL5dSvlA|8TBO zYD~Oo*CvwxEOX?1ltFTWlMn_JmNoZl=4y?H_UvKbrdAiLp794DN4f6qY}h!dZglo@ zs{uPNlChDTq6;|s3Q1=*(RnBo|?*adDGVPyGc%i!|9A#Q>K3N=c zQ_}jDjyG%Ivihn*;ZbLw?||zH$5(FVHKl*;{Ky+TLXCIA3)V6Tn&fM9i4IFC&wxTH z%2TjA{BX4W7YKEv?RP16(9H=D@WSh3o0c}No4|Rurz!&S34H6930J)YL}umB9nrDP zF!7o-190w3gqF6(b@fR8TSY~*RlY_6hy6t-=_|We?$k#%k3$1<0nv`+80mve8*6jG&HM7YXOs1CvkhJ=EY}zP+ zIbf$6cvwpTP!2_=4J%TMg$5rEB%@6GTl*jZrU@H>)OA@}DSIkdhQYO0i=dm$Qcyh@ zN`hd0CsDXD$A4GLd()XCq`rUQ>Cr=k{0EtQEm;7IGy#qHqL>-i`Y)a83y+HvJi+<> zwwW8ZP}0`}Su>n;*O%ONUyx=MQI*yz58ViUlTjGkg9Jlglo^3V{m$(+l_ZN^fjLA>jj4d-0a!|kZUyu%#~#BrxYZqy~=mdu#Uzs~Nlrq^VvH(dPMN4_?J zua^>n&zL{7R09`8har_Gy9h;vQXg z#9mE#+S%RCib2Mu{B5LxHFDPk|3Qu#3rY+8;`tfhE;!Rfuk-(Pg7eb}M{)q9NY3Zs zD*oEuoFE6!$!!z@X#m#0DtO%9I{JnDLU6c>&e1|+t;<=1^9}38E+sonNZVE8r!(Y+ zS)2G&5B&XYM|)idCigv2b`}FbYMc?rf!%y$T8z&Ck=b+D#ZlnJtITleje;v{hEu&` zumEPP>BaTvp^=E(+x&Tzfb~|XK7GuC&BjZna;Vd(al-uAh6M-ci35>2Z%Nq|kx(!j zG8#%%8q1YWeTnl21r^o;kaxMf?){CwL(?exp~(fH>GB!%Z56={R;MrT&QoO+P^D zcyOuAX*N;lL#dkq(3QPQl`JoufMF)zjH~o3rxB6sY?Qt~j(^l$t~CqYZ1Q`B7_=}~ z=OGSPTutM&oGGl(NqJ(!tVnRupCZNV*)%A~Cfz0(A=)mIu${bpDpd>7Sc+n@6Af>B zfjy*(lqrX1HM#R1fHm214qO-2H2qumggtc8R%gj!ByH?&lX#AA3i|#W^JXn{iimD8 z^BbbAf)mTNMtyl&-5rJhh;xSs-Mt9>?M)AvJ$e7r#u4XD+gL^Y#z@AQD;RX` z*i4a20Zybbg6y)rW22V-gbSscAJIW?(GW(It9o-roAr z(xKSlhjWv0f$0)cl@>bdv4*VZz;nK$T-WuIGG~O-VM`Y1BqPKAfPY&e za>L_w-@pw!N4Yi6LM&3=6qh4b_UPj+ZO=QdeF^lZgaS z)a`z`8Os|OyGWRC#T&}t)C$H0A0wt-tYnGUe?cJu0KJbI@rq;$>Ad`6Bsz4+BHcjI zur{j_i-E(SJIq(Ha#as6K+YtV40EQ5P6$aQ+xGf}b?{{*rn3Z{`_%#0rNK!Ql^0x{ zHrf7Tm7>n30s%`dMiDLV;A-;oM}H<1FU(TD`Coq1l?ynJFrPS@Xxpkb+g0Byy#fZg zxsKFtI|sGI+C5B$wm^qozYyO2i2yDPj9E<2bbhcnO0hlipV09?GNeUt=dD44#?~&r zgz!$keppQ#+t~?bjm0zreKjp{f-v84I6zTr*tGfjf&Z)bvA%#xo^%=5QBhHhtM<(c zM5|<|T+amJI{Th(xi(kbIS($L`ef_%&Zxm07;_gu{4Fp-f@c#tBqDm5C-KV30HhF& zyNT5`mDfo+r0|u6uJh@d*lZE8H`!n3IgW)6MxB<-qt&Howo$6i0?Zg+rBZ^2p+~H| zHjSIV0GcbDmeY1a4UiSjvf4D<{JA9{=L%tf%ZUI-kewpnTN&>jhWZ16BUpRyABF^& z0l)zRI6^+bb(;dw2a3@oz!8Z;2dPWWg3GK7D`13yrr!Gt^1FQx=VF`T4v&jYXfsEB z?83kH#3}XkKiLpU59T3)3?)#(iz3n;Ec#ny|BhwB-(`2g_SoT<4%`6eOVl>iFcgW) zd;ASh$-bpF2L`=hP5qyoLV^tt@$O;}L^?{Ji0E~eyOcnEye zlL8TiEX{(Ma|(rGa@p4{Gt?!;$0oZ1KRsJM_AVn<*k>M8kC5Z4FJG1SDOvt7r z=`PZajg(0*o+Rm4go}DTsjBxV6luT-yQs4WC`eBhhj-xqH3CUe&>!AiLt2EC>K|1$ zX&SYKMRw^Vh*~5OFmcXQ7fKo}K}JF2-vg>z=6x^Rd-C^On{(7lzca9k!vEsK7?bt| zdFJB9?)U&u4>HH1yc`lj$^$ZszA|%JCsYkLMKp)Id7FW{Rr^bp+uGrwx=V@z3QPy2 z9TH|2$wmdGeJFIp=>jYXkHDxJ%xOmQ|e3S!0}%TwX{loS07MHJnq z0aqB*vg3zQ8&gFRR{tJFyY~dYm)NsXH4~&QkDw&u4acerj3AQ#w?9`9!A?PM)weMK zPR)!UJ|SgJWCy{_e*Y|>%EwXxaxhv9MLR zblZ|IZ8(yC(ldai8OZ<(N0RjZcOUvGg8E2N&LHdB?>4dLqe7%6!|%^FVk5Y(VHhpbz1ZE-Nrf~wJd4&EK zH~k+!i=7au^^d5}O;3o7%hIecsl9g1md$LdN94h5v%+u&52HqnQT$q0$P>Lf4-gr~ zFnnuj_)OLiOOc6_3E82*&VNF~W2qH1S-yQT(MzfywtQXJxBr$8GB2#YM__ z9AmOk&rlj9yy08KGQUI-e31VxBVeuBW7|ePdl=$Nuna~*zyb^#)VWEqLj3$T#pBZ~ zHVP65D9?%p4M_dBFEqyQS%yrkpS@=(dHIj1TQd1HAZ;l3%(*Jqwv$Gnh!XUMN4IA= z?ke+AN|Js;Dpo6dpWr-8gA(P<3A`;Q&Q_FQ1MuO0#1al;{=GxbP=HJSkB7go_efsW zUwsW-B;^~;c28{5=l}2#vEF8H>cMkNkHX_kZ4GJN5I|WZ>*ognwubq&||=f@FrdBxKek|MWJz z6#$dE743NO?*`oeVGe=_(emEHy^;dL3;h4|HlAkyCUp@U@aBJhLI1XafYJn$<7V3V z?jrL#%^T&hgXj>nTt0-9JZuif2ucHsUmgXxoM#De;kCmc2Z%}xu;NkfIxL=wd&BW; zBvS8EVt{oKsO0ZQa01JsE6>`(avTM)dP?1DyR-l?rfVK+8=g<`{8mePY`bYa95{a| z!u0?c@+sVXRJED@5B*Va5JxgPD2g&JzED8m7l-`|G;>rPNELHgPl`DV-2mWH$36btM~TWbkGXtVR!XO3zL-|ypqFs+Q5}a@^Bgtqxxhsef4<24E%rX4JNqWkfc^EG0 zb5BDP=R9?>=OvPlW%Tc^_dI&woM}h}6f0yvDJM|uQ@$%Cv@C~kx9v_p)Yy7I@&$T)u)vrCk0+Asr{~0I^*&i{tHb z^+S77W_^j|lI+;_?~vyMHZu;&(67}pcn@GfXA*vkI7ao)DGO?4-V5JKDh)?chkG&N z8ay|3Z+1r>&R`MiXVX^()n@{BX8EXQxzc^9_9jEZW2MqwQt^Aqf^uRHXIAshfOZ{B zCjhTiL?`CWJshc<)}iD4@`;gNYl%uOh3$1P;+&w?6T!ht>#8}G&qJ+0u}#OiSW^RF zZh-16CWG9K7oKBFzV?ZSlqLX-8w1gi!Hsr`0h0}|NofG{EfDfq2L8OQR?k1OTkFDM2Y zOepUkd5*m#c`y{cI!}I&WqPIQ6?Z!k+-g~pcs)Jo9(~)uddIIH2h4|%n6DHlK+l`a`m>(?N&ABtJ1Yl=jo$Lso<`UbPu+6% z78p6sxw%zvfBkHSN)UpR)g}hxdT*sQ0;3#t?i8Nafij|mtACb$tTQePBjTjp$+u}a zed(4zg!6nLu;%fwAOpV-;6fQ#cC|;PmM&Vz!NPYi(76RVN_(qKc8Hdc08Dvz@ik%sb;oqbZ^ScRIDBueuzgctr zq~oD@cUNWpoZ!sRNGg%a*{@_R@8UeIDFw^a4B0w?{_)$LR(U=nY>x_HDqejw8EGGY zU43YN$2fSoi3wgkURU6s^ct)KU(tfG00_thN$tL>f5ya7dG>HHsfI=G?|!x3Z^kRJ zM6Pk9WsS za{b(4)m|M`tr`ZevB=^488`8$u9usc5Jvr4Usz8B=iDd`rsy!-tNd!Zz{RC;e;Dr; z)TUBDf8NZ(5atf-;Qd7;A)&V_T*c1b$1>MY|6`dy`_(BNZc8s-)c~ED%F(`l(tfaM zcnotjs2zN?^D!b~;>HraWy21l0_wr1@a{XU4R=e5$D?yGpc9kd7-U6pVC`-qFdK>r zzW+Nz(M`xvDnn)%wZhwaETpDawLyIXKM1#nWxnVp2{3i8<}t^uHsmah8(|?qVNIO) zJC+qh>NmSZ5bAP`E0nxXF1_Jb@d(;?PG>Ul$isSGJ!ugdh8(5yLY6?Fbb&5yNtX;$ zqB~p2&>1$E;M6v4P+4@Ue^$TE@Tsv9$qt0CUDsG8bh7oJ`C~|H5B42HV0txPn#Ys6AJsK zf0*7yo7+SZ&|&aSXx`0NjyC5oDA)5a&)3;cn&)T^B~OOc5(W3i+f-_&0Q&MB@fzmk zmk;#L7|g&^Bt0w)NwQtwIj_R1+bDzYg>I+}fD{Rntz6w%&4Ke%r3&E?Sj>eT@i}`-J!BN3Za(DCU3;G|6Aewfn}lqNbN5udd#6CMPlfi{BJl zgu@-;9B?d?y`%S|p$J$jK$T2ym+OxOCFy~lN3%9JwwG-Wg`JVG1z2Gs^4M{=Eg6a^ z6X5os>lF&SaaV{8s2#VQ7;yXhET8-zcy35*93aDu53iTC9L3N^--p$isYy%Y_kvQc z(qr7;o=M+;qB&6sK=jFnS9>q=EPt17eX6*q+XYJyX#ZNRL{0wHt-M_&-j#P6enX*^ z?`V#EG(Km$8ib7Er&l$!lUKcRX&l2}R*!!DC$D5|vtyZFe<<7B)Vi`2s;K7k(&=)_ z;B1(4AMKPJ6dIwbKWPoq?D9+q5BZY>bh(ll(W!W+6|w5p+%h9nZP=H+DU|!!WY|+= zHG)Ls?Zn9xK)B3Q>Pc;|-we5MzfmREB+zM(bl-O(Un9tVD|@|cxc=kjfryUXn=M_l2P;gemdT z&pcp9tb|@RgIf0Ea!ceQ1TVh)Sy0d$mf(K>U?UMp9E&Br-~$y{yq;I4{%ux^z4Ab> z8L4eMKXj*Mn+mG>@)QbsEPP&n%!hT|(V6H3WEu=4zzv7blK6od{DS*DU-y}Eo;0Bu z)jiIMn(qzcNFJ6fsob6g+jG6W5>&Wm!VmOIdb1B z7h?ntii*c-R9_USaVJu}Zq1YSN}8%EpHTgyxJVk%S-C17;Carp!QQ`>Fyg@()og*E zu>Zznyn%nL^5;~3BV0nj`mJs>_t0iG?ZZ(0GEqt>Ez|Pi5>5;H&5hM9TINAWDc`JlVE{Iw%5#9 zY6zIs42m799k1~|Vd7=!cZU&dY|XKEj5&W@%~f0n5Eg_C)RMe5N<_nQUjTnxL$;i$ z9L0Wtjnz>A;SMJ05M78~dnv~AynZ>+J5US$=J|$ATNx83Y;RT!n0DnQhZHQ^GVoiX z)CQF#H~eEJ{{0Mv4F8}?e2bs$-Zh!zIFX} z9fENu?(^IEhXfb?i~673t>o?|*HDUV422kuh+~-;|B=al4^<}Y#qxnfbsTQtgPnZ^ z#(g$4iN|?hpqn0+5a?_F_BeGYSoCAT;df#b(T`8D=CYRp+ziAT?v6N1vKR>LX3c(Y z!i<3XfZo841}32kbN7I=#j524UEUC|DCs{VFk@kz^%lLUmO%6HR17{Sm&*_@A4nIH z-WbRx#`E*Tlr5^kXBZgvGnwFK=*^>3r&O4_Q2wky)fdh6jcGERrAJqKd>mfBi z?8(IYafZv`*8#7iIH|k;W?zC3OTJ9d&mfM-j7HNq`k(un zQfd7H{(KU{{m`84T6eJtxOd9ZuhFPbl=tK{o0nC`TP*Og0Zw8LJayV=5$h7Q_Mg)@ zMYw)CF!d;&DdLG5!}avov3siHYWEGvRy{__V#kp88V5~-XSRrxLD~)97>iM8-fRq- z4oZ0FFE*idGt_#WVO>@)+-M6^G))IUi^Sah|DGN+I@nu@+n{gZFQ%p2&;dQoIOe8t z8XAClxdzh&2P)$Ur_~%NPex_2oOO-u3g!OS;JBGlDx*am+YfLJh1oCw09CDg{1v&^fETVExwV1hI5B4`_z}`DI zN^gEF_w>=#&tmdNC<;vG9D1+}QQY_bR2)DqAu2|V$fKQjMMwPO_1lT5Bzb1kZlEWh zxoB6JN`V@asxX7WQawUQYoi~_M)z7K0YI8uJ3`XUUNOx#K>pZcX|BktE{g`1JL$0I)k?^1!dXwul3CABP zT06ASVbTtcQb8ZV8YOo}F&Z_{DLk8n+S#g~^M_pJ{{Px2YT)=7pGdhw0OgCPEjAvyki;ca@efYtj!dqk0Q#+(@w8&JFJoS*v6Rf zD4+bsC8L7xVWs1dfc#{^-2$gCp0z+WjYEX)s03qCT}%Gv-J;2ugi4s$;T*qFZW6L= zdz&(;?E@}-mHEC>(n*K0#znovO1_O^*1~kZ$j6wK9P^(Wg8G835#x@2*n@KEZK_ZW z=)74KTS>BRx~aDC$F~ZYR-fN;Lrz1&Zp~W|G8j~?T}TM zQ9o&k&(w!``OYWgEDC$$H}uvjG3;lfJTQ>*_!S3U5ZPh=E$L?STMxsrd-`P3ztH!- ze+niQ|Dstp3`1%9&R<1p1$GaYh?WG`jeo2r&*s@P-YVGMRY}D#=Fun0*3xxO!Ilu- zK=$h_FogS~i_v#nk%$%=^AvPeR~h0ne#5?R_lc*`S=cUL6^@W!PvaReHv*&p`;`6* zdH&J^36IvbqlMqWrq>zCy`Hi;GRP~wxx>1A5GjW<{Nsnk9{DW3f%`(xUHxx}qr zo|^FBuP9t!`@GYOCa^b3r9c)_Y5IZ9jIZ7k^~aTC3c9;WwdwEoA{Tmw$=IP@8rf)8 zk)O2jw+7RVG?SxJ&pSK`fE_QhWqqSyYZE8XA;pSKDKNcFBN&0itu~zBLKbj&^IV{H z4^@U-is|4!N|3;b{dR0o1_4IWEsHD(c;h*cImX7&#$L`VSp@}x8_eGatT^er&xWxsiN0aUnEEP;w@LTnIJpm*YibS2*m6qyaSU{JP)@svf9-G@x%* zZ-$GsR}dYwT4lH?lfL(%*JzTZlE3c+JrnXeUF*u#T~6l@ycbQi(O% z#od6hSOBu919W-0Dr=I>W!iO`ItODi1J-v3DrZ0ofx;}w<=EZ)!f);$L|=Pd%}yoH zsEn{G2qc{)F{jc7tp5qbYvBKxv3z+JbTugsHq8o^y1Z8`fDfbaasrH1rMm{Q3Fq}z zPj||TGC=a!GImUvLE_ZOLw+QH(je461f#sYYS?T8I=GFc#A)sk?~k1PCOiV)KDaH; zUFv*TD2P+kfpGdqE_gn4|8ck9I%y&L(Yw^QU_D~?VK9^);evB}{YmWeN81JY1dOtr zNO38#dyQQe4q^c+-x^cQ#2x2}P91kD$V} zXcc4;P#gccO$Je|mROLd!4&t6-6g-98GsxxDEEcdl$gS*x3M78Z#*48F0 zg7ID^%BzyXL}R*SPN4(SVG+U(-F3!yT5a>>lEQA(qh?HpqUK4k?iPZHe4(h^Hq)LT z{Pl7TKg6AnPFjL`)D7HgId$n8>NHeDkQ>Fs5^|yiTQI*?AcZt7YBvX@v*n9@iKAGA z-Z)1P%0pX2-fuptMFc53(>=B4sm6io_|iXEHL~3*TzqE?y=ioSuvL@wGrfUh#@k)+ z91mRVF0DznVt4wzvtRM6%E`4%8q?$esZn;5zC0YQsYs;hTEHVlHvtGc;!v&$mW7nY zuynZ5sj}N5Zj<&%Pa+3NL`osZ(n!s6Pv#E&o9K34;%{8WQk}!cWK#2~R#+_?`{HXprktc}e}*h!5&}7UD}eDG z`uP@R$o}Q$u4Ke8_L|s%g)xez^{?~$W1#*jLdNoYb>)QXx_5a3s@IBKO2|^YH2Nw0 zH@J<=PZvwn0cP?F<3kJ_F_3%dToEH03W!~!TS~m>KJyt|TM!NSa#5cwc(*~p?&fI> zK64xP7|%t=GMKExHZ_K7;%EVEwH~?Q1}eHtReuj__wiqLHLaJO^NP^n$v{rI{-)ZTIf5IY0 zh`w(gf%fd+lbp>FiF5e!B8@@c{qU33^QWq*#1=v~%>o2#>BwbehI9`@)r%3J-Vm{x zfg*=Q!$876nw&wmx3W5Uol%pN@sZzoFRQn9=hAvUh((E=8;^B*T#?0P5VU^TLbnFJ z+taL2%#6$ zl&*ivG-{~al~d%?I2J*^o$9o8=Bm@&(5qt}QXJI_2IMc5t*tsoj2Q|UrNLZh+XK{y zuP#RCm?UCEk;K29Byp)w4K$S(f3H0RNqRn4(Fzs^81FgUC%*`@v5_Tpln}aTNg{DX z)lZEWVMi78!WR{q>?IZm;UjT{D)_E#r&~NMs}K^4Xlg7wR7P(WlVOU`c<-_I@dt-4 z-O{n$XhDxE4TpF$zi_TaJOXgx1%APgX@kS5Dk1dkfVo zB5+kZWJI*#yC$q?zZYdNNh2WYp~fa#7yA`$8lq7_(w8S9Wx2LlzP_t*K(9AdoSQ84 zP6xl27>QXTI_#X^pL*&gu3(Uyur=pZeSx07oQ14-yH}kQVTG^W$S;Po9a%I;q_^1$ zF)i#YLp(qy7!ZS6&(F@ac@KKVwvT@U`vlzTzkcC5LIBZ`o6#Pv+!27EWnY zi`tP3SE<~s6iUPhvUL@!+5nlB^)ghXY}>&=BLj*!$+Y$Zspoeh8VU0pO(f0X=0p!p zknCweh|Wz{;#jn(}(`X-!ccB)F-!8Lo(;rV;nC|Y}#Qlwf zbm>%!+j|4#Ngp<B*3{_icW&mEU8l zP~=Ix2;TTfdUD>q%K4%(?(u<~wbOt8@JE9?UL*?;{c=Ar0UM6%E={X{ZG^w_6p-6N z21mXHH9Y{m(u}SS8mqsrQyuc5EIF-CUn0e5dawImEaXp}pZ@rH3O zEw8~2BGdR3f|tS0?kW?eKak^|H5Zv1HRgx`g5E9278LP9;Y(ANZ^}@Qn3fW?Kd<9j zOAE&$Af^W;HoJJ{1J;0H@rmv&^OacV6o;D79~2L`(Czoi>KHXF&8zakZ=*U?eduw_VOdaW$RhcjfdQ)T`rqv3<#nPBu$#WwFZ6a0y>g1OjVZ!;9p z=Uqg^jsppU@Y7XAoFj-Hqkp|=E6GYYZal`a`h1fwM<`|V*644>&PdC0J%@!QG8tL* z=Q5;t%@?TiYMtN-WwxhSbSv8t~gl&0TO0{Il(!d0j5sv+O@d7EZcV)qT&_2h^(pvh__pfxW&&BD0)KFTOF0nU${B1DRRx5kQV zcc;NF%Lzi+S1Z$0J%sB=oP-sIZ%+1zi0DF8=s4ET8FPomBZ=m-rY8%UkgKOkyHV$lbQvXki9NxOw<*eqHd=Vl{K?u{yh8{ZKc#wiJZ%!dBL= zPA=t_Da@#qH{hcC6{fKVpcI|JuOnw0r9BHoCCCCvM<#sv7(@LpvTAm)VCAvQ!VfHe*KAn z^9xJLm4DL$)=6HP(ZoBe#mHh0KI)iXu57-HdNd_10%XvqQy#IhD z3=Jg0WfhH@xr@1}>Am}4@j5_f3avvsP(PBBA*(KQClr7w@k%A|g|gG`r7>^L5Y?jl z=RWk%m(1jWxRMX6#3_M>b!S*YmFpjj^7_JWHl~VS)MgdhFeLV9cTKX1h8q*X9aY^j zb(BOlg1nRJRq04)To&8i`vd|nW+@cN>`|vGk2rGTKX-RC&)m`kuKx0nJwfJ)ezj`Z zjhW3keD74Sz-gGp%WGNXNc0#eh7^pPg_)nAx-Hd05Ku?NbmBy=kIHRF#94eCwCcSp z@$HJ#KPpHq&{Uu)?X#V-g1s~Xv&_oHE~}>h#{<%PDg1?5m+PXb$iDSRKEpooi9-1_ z56RY-Lz_SFJ^Hs<+hP!(XBP>7F~fTg3Q354IPo+2YFYCY)5z`v6Qp%cFqrr-fzN}( zO-^iU?=PI-#&qo~{a$vfEYmrrD1^nRNePz`asZP2B|TnQ9vsS?CeN@#-IWu z3}_MUgGstXFYF+$RSvI!8qu5aJQ-@R2aKs-NbtMYkye9g6;x+=EF+<@05yi^-}y-nG4e-|ySpm^^grssFZrVp(Bg zAql(bu3f!c=g*t} zIn-<2Ar%BvOJY4qgnU|kFiLpj+vUXT?T)K>!#QRUH`zx003Q|_nXq5fNZ*;E45PwNByd|f z4@JSqi)AT9AyOoV3|Kkix^AIS(2Jpc=|rkJG0R5lX_RyQK6@+80p4E^{i*IQGe#Zb|wo4-|!}&Lwx+*Yoaw7YKsDpE`MQzZ}!8rJ|Ou^Wz@g#NyoL`7Z-#QpR~ zEm}wlQZWD<$rIA87HLftTPs=7IMmOD<~w7e*!PVYbyK+<&-F>p<48ohi~gDYg4>|? zNu5DqVtAdp6#7;coIvJdO72ZY?Nm}mthpW1@n5|FEIl;<#c1u042jptFM|sq)HYtv zP8u_}X}_`Nk!Q129k9S>E(@!{gnD?qd;0+=e-5_a-OgG9b#4w(!rmU+1o?o!D`gp; zU(hNvI7YG3I4l<|QR9QZvw=}6s^I$-j5%Uy6ZKsgG`Jn#c?bfm*tLSOyM}7So4zuS zIQ04;LIvJ`&Cb@@Qxg)q$hAQOcXYG7#Uyk0`yup00TX>w2*d>S%>L5 zq*`0ks>V7H-QCMz*Ucv%b4&n!Ey4nTni-=Mha@k726B&X??&0d1OboG?IWJBE-1g3 z;B(aRZxz;7Ii-9)k<5xO10>^uLI}cEQ?I|5Z>PFS^Wk5!APWwWfJy(b{?}ST^R!k} za66e3O`Fv&mV-@%s1cX?cAdX2lHXl?pt(LY{WYk;o8$2F{2>o(w?V5uF&ty#LTb+E^@!Cc#%n%gE%UP`dUBW(f7y!-G0WE?QZ}>O1oN=jtfmj=E z3ACC--Anm51Mn=?;}K>TzA zUjAE6>e~6&l&jhU>E;a{14js_)tDmD@LcaTqf&*A)u>n;rXIuXrn+0^B;QRi5MfNzB8H5M zCt@+NNr9;%oh;`k1@fCo_+9{1_ZYTo1HZ8~(?fWwx@HHg69N<_skZpRYE;y}`A`3*Or=SIlbhJl(9HJIEi>y0omIV4u*)9!aVssdH?~iSqL8*}x2(%B) z7ywz%^qMFK?bE~U6>b>A&M!H*Yx3yEP@M&L#GD~66XnIB2h7hdf8ko*-TfxVyJIyL z|C3hZ&at0wo8Dn>QK<4klb#H9~2y`Nr!{Z!|qP7J6%mWq8zg%67y z+wLVUGK((hw&JylHxk9;&rpzpR;$T5oPXF;qt@V^-SUz|y(-nP9Q8{p#FhKDw?;7aVCY&qr8Q63(=1rbpMp$Gatt>Ju>kahoJ9%Q5zZ8?KI#o zF#Bl<<^niQ7D#xH$(YQTX%a^Laj)HBOdB&D7HwCBodGf_8z&=+6y?I?LD zz)O)5GcF~Qo8V?LbW%R(#ZoY2M12v_rf0QXz8KMAn&aiJvQecs|E6xCKORD;irJl- za0W0O;gQe(V?QRl0!T!~v-G=sAAj~0=3)aiNsc4>UPeG%?^b2dRN4gD6YeFKh~Q56 z4d%c`jc%N}Z&lkW>P>DEOf0v zk2B08Tt3K=^hc(f6%9tSYLw=APXY=-HcZLEyBOO8{La%L(8XZQ2$8;|=&9J3moEWG4)^5r<}*m9`LCyqbfk) zCJqS=XXQG(@;#QhC&7U-o{j{{EK+)7U;$(UvXV(j<=dVhWNlkWNi#j2{$t9ES0<9} z(a(-;BEE*nE-j=Rl)PEwygh`4LqqP4T4mnguC#Ook96$>WXNS0 zv7N}KP=`5Y`n%mB#D^ztDzURU>jy}1kVqm=`PSi>F!UMN+<&xMzLCT1!@@$vF=#!i zps`9xywZ3pJnTjCNb7>GO$xC(cSOG_jp}_tZ7CvSG7`t2L{H{x1W-7AZ)GN4!%G|u zPTwbd@2Ni$c$%c*b>I{YF@2--si(FgOYDkGEgL%sm01l#mF-m; zte>eKIYJ*LsT*SNhV6H@(dmTcJhNS%IV z8Rpa@zYcT793?}Lu~SSV;z>MtkyTacWP&2^$2_aVZT4K!YO&9fKkO1QU9sOs!a51bLvp$X>i>Bi?1{4G9})FcMg_XS&xfbX7>~z_{k{aiX*uE)oMKOvWav| zJLUl>&dSCDO2MV&mZTX6pv@%?AlJnVidzN`Bs?(Gv}Kf#2?>Y=URnL|eHG2<^oEi! z*X{1&HZ??4RkU}Ipe#j83}x&i?QiW$HKs(9YiWoRldO8msEpA%afu!xsG(`|T)OW* zNwv1;ley{sr!kAQk2faMC>cX}Qs8jjWuO={SJ*J9+p3UI%0tmsv|4qTJ(yF#VESjk zPP$UP#?n+|^jf>oOo&6PwvXH#9L#*8b8CFw`30>AJ%F_Ai)@+216dg{$n2%TF}Pld zYFOqSx&osB)PbH>wVky#IXz>-Y}%v0Z{o##n6X;nPF9sAq)dbETT`p$0SMG`xpU>q z(b*WiOa9#}iFoj@v&V$3chmRJVuUE2+&%(o`QT=gBw9n@PsZ z1r34WCT7eS@jfqrrWxY_Qhv_}=6y^{UER*cM?%Bc7ad?9^oc*teLc}#YuZ2SPq9HA z-2;J;p~~N&_ZZ~2$RQ7f+49zNwPN{REIMP9px6HodtVt9*Rrjf5D1>&65JgEBoJH@ zg1fsz(8k>*xVt+E!JWq4CBe0EcWs=v*yr3Q=iGP48~gpe1L)D+S*c!KRa0utZ+_Dd z=wK5uCG4PHs&V4iiDZr!6JOI~K5K_Z96f>#R0B81bZ zpZREd*-X#0psY1r0AC^{Kvd~0xk!Qt?%nN;BnK$uSU*#$C^q@|Ycg6%iY zX3{Gy_iFIZ{%jQW^3H?2EEEm-C!b@gjb1=8G2qbLYC2?8{;916AO+Vhr=H?K24t1r zHxzql2sr+azvGC?FDwsv0C6MAh_|Nz>TXnP7~Bl-?Y<`h%4av7Np2}$qvBhW7AaRZ zmRx%#u!3YPl{{A(_3z37BGeIbL78ml9T#T-&t64lIZDTacokj|9MWSw&1l=qt%%ECc0`^;yP8}2k6w2+mSh* zA$b zy;YKFD_17InQQYW2vHg<<~4ypZs( zThyp*CUPKLC7*OSN8IcZjMW6!GpdyQbi!#uyzHh-AI4-sx&VZszARlHj}Z0(XjIib7-mH4y@07yYu&~~yLnpi%>|3oZ=LC6WYYzCIGF1mvp=?{THOkI#P`U4C)RV0 zs8Vbxx%%^mS|vHZoo)oA#!Sjy6;y3FuS@s9~ z@cYuta$lgAR-0p@<(wy|8R#UKC-n>21+LBtYWaEygL1p1*BJ(fX}-pKSD8WfxnP@= z4YAv~m6FvBwLY|B%R=L#$MH!y?o8L4U6C_K&GuEvPKoasHqmKZP0>IK9ZFmElr$S}!W?KIKJ(?qOK-lptN-?ylxQ-2kg zsN?NX&UX$}D8Y)wEW^Hw(cFO>ii7rbjPnj+X5%`_8rAE=>o;Ml8=VC!SlAdhnZDwUgeX;wtmC9!tJ85?A;+`6M*nex70@la`;E>q zAA180U&eU$3>kZ5kK6q>uGc=s&Pmne!4ICO0|A;iGY{BHi$rJu8&ft z1@&1!yh|Xh#GAh?+P)g#vhL812qp5YTyPnSOe^c~Dxr@Psy$H(OwTn&)X7oB#%5T`GQG8d)SYq*Fgk z_IO{Pn$ES{kw}K1#{dd7HkK-s0?&d@xvBwWXIb3%|A0MY<`2 z(=aGASBqgT=E@KVP^C7%@IZJ|9kI{5UZ>##qy?5Zjsbs|d4sH;QFKHX?f}nslFTwd z#Y-sQC7i|nS21*q1S!@zRnE~}awTTIL~Y~r_6Qlpy-J(Q02^PEPb*EKj~ z+#w6k<=>9NMJMPJHz(|0e8Rrx9A`9rIfN*-V1zdbWffjk}i70J$4Dmdh zx&U1mbJut)1>pzt_y#KkeBnGUO4NnoNCfNNuU1;-U@EP27I+tQ@r31{RabAezC35_ zX|2ZfVA)AF)}I=~0wpavX^_HoY*RBPeQ)tfq+c=};(OTcu?dQB)1w<#Qn+wRiQt7G z8`|1hpjxELZPmaf;*uvZoW8bpq>atCXBx2~yV5VscQchpTp& zpgy`aJ=uPO-)6vD>Rk_;utUl08n%;&9J}o$=up#f#Cd|W6ho5fSjKtETg2;_ed+?q zWg~Fn*LRH4qnU^9eW>wY7x*WIGF|I}Jg#n32dS><>gh45tWVbpZn?1;7IVAA@qHwD zrCwaeGN_h`W#7feOL^AvB)dd%(og|^Q30PM%hpi|Ox{hFm&V8>pH#kahd)^~yOVR@ z&0Z7{faDb4M(oUeoBpZKm*Kfn5hGD#m&MQdD!H5By7xU%;I_<_Wd|5K87NG6;&`kN z_?qN_$a5WdeQ?M)eZl9rte2_AruDq%+bjUiGe&3s>Yepm$*23Hr`_%1#PlV$76I#A zFHWDTeWuO9M&mH_pb;m_dBa1!aEc?-3o z0*Fd|K;nxj%%#Z-V(X+CJ|6CO3Wh7^0egAFmR}%{^-L+;=I*aX@STMWp@$gk` zuB#V@C!@7Xvk&e^U6`cSSzZHVEju^lndRn?lXO^K*lUmT3c=v9f6V03SpD^T)IKP` zn&jc#-jI;zFO{PFrnj$zoWO$T+a$87xkDZAj8eu6E?l#HobO;ZWo35Jer{F@+UE3c(5-*mNvl&>l&L!fbZ%tknZn^v`Nv4~WQ3U>x<`$h8vWmmSKE z7dGo?ehf(H(Vv(ZI%Urc#H0NX`J^A#h3Y)#mtsK6pE!7|R6%e$@^!9eJO4mY1=G;* zv`YJdJaVh`YS;W`XWPnvA=qyr$i5D7(S89YX;K*#PJ)M@xi1&YE(Y#Qag?Wh$Is!B z!#L+KYe$Dl^&5`h&48j|(qL$*s3X6chNVi6wky09(Md|*FC-*B8+V}rm(TR-wGYW- zV zS6KQP^O^GVcNE(_82~Ziz@@hDS}6)Tb%Js_AMxU|O&V6dn1);7jspA_DZSk)>_+#5 zS86CFn%hnp(1l}@p^AD4dbl#E;|qE`zO)6=GX=g>3Y(kh)OKNKp4oJhFa5S;Br%sc zxUsivN<2#{0cd00*YCF0EuL~ar^*eH-tDU4hwe8Ez#c+zq;`03#WacdzR-0KE4sG8 zAyLa^wm+qage49xbS*1Opd~d|Qw?(6R(?BW+t+wDGM3^c@&4ZhZEJP>c?v{|gBXV_ zg4s961aJW_MN=Z9QQG+6jwDrF1rLcrR^U8$668|vaBxur)RcYtnrRcc;-Z-L4rTur zAqer#O8RKM)OhIbgPzpLm6O{(TFm^)Nm!9r&zgDNefgIYGWSL5F4X{teBWL6*0^5h z4@qC8kL(*fYtIi<9EJa2j8z|vwK2-2UL@8-h^ZX+Ipdm+t(bP<-rI{RU z50K{ZVx=O#y1{U_AQU1x7aDdr71t*I%k(Q4p9otQ{_9u(x;&`3N7SCkz1LDR!@$y= z+*UL3is-YpZuUMaUlpQFvu(4;73U{AjjP`l)3e_IdQf^SB+uyswwG6-L9e zZ#Q^83aqr)dS0g1`Z?=pp12Eq^kdW^hDRt_j?m0NPRhyG!wNJED1~M2K zH1v`gcC6Al`<-92O;QN$R1-WRIa>G({Z|aHW@yRZPBAggFl@%ji66rc4jNM6nC~ac zi3M!R=)zF-M`blh19Z&T6v$fY(3|daz6y4avUsIaL`nvT8BSM%V;bOpj$OhV?fC%{`dg0~A$)g|HT?od~3 zz3Pyu1B!hmYkwtB8uk)Q-I)7o3TVDZ_l9aa&;HvAo#!J{!zwO`=LP&LO36e<=L0p7O!p(}Q2);rl3Oi*)=NLpa(1Q~ zhG2*k_}tpgR(|HmfcoC0Y2uXr_E!575Gz(|o=_lsqp8HhhO}tSqBIy)$P?2?;;vR@ zGLpM5Hr)OlHLa$0Y{5b$YN`Of?WPV0v(LO7if5FUQa9Kod{4sG?zhdkJX&=oJU}#9 zMG>E%tN1;Nu(XYb1i&TV3RpH;JgxSDVQ}5hqbk32jk*qm>J*pA zFWKu_m=(k`R-2BQ)0L0h9m=YkSkn7oi)&F7QT~3KAMeeDVzwQu5a)U}Ss z!x0!i+m`a)-jsInpnHC3!&E!yurH7~d}ltPtTP44*u$@0TwNoaX&V_b;E1QSQeAfW zas7BlvY7)<@;k}jj*d}g9;aS3oRC8L9cRt0zG-S5)PM^*)@8-qUzg|xAD4OhN z3_S9IbfIt`Ro5l6zs!LxoF4?RZ{Cpblgm2quNDVmdp|hNh({5RNqDi8!{(HYfhdyk zu>U&N<*tPhzRXzhK-`vGY%K)bf;^1WPbHqKLEWWiKMYF#LDtAqbj|}`M zrXBrK6$bqWLWs_yDqowH+)Nu9J$|K}RO}se)al{U?`b5{#Mi!+SZ+*-%h2-8} ztq(;Moo?K;8^GVRCcr^m%J+yF9)DWZG@#wTvEDBA<8FZ)%(B|YN~Ak?a-kWRZ;OI2 zMF{CEuVXTu(x?AM-l=k1gHitAn&WEbn(y@u;2u*L%-3yEkZg=pU;Bk%u0tXboz*J$ zSxVdB%&9w{vAo6~93m9d`Q! zIJMu4D*s&W;xZb)-0;O%8MFR>?1G?q^TqO3$^$OcA ztm6yGBm<2zw(}sxY+;1kS3Ue5QglF? zBVW?lUk8hIqPa_sG|&JMOEL#9I4TXrv$_q7eGvrSbX~nMom?AAnm&Hy5_nj%DYDw` z1ATWEMUAU5o0ydsB00@ye=@Y(^0~f~(OcUt)ql0=?IOh4y%3rYy#d*XYkk> zvf`V-;rN>fT`;_v%7wGYS0?G2*DJpMtXO29wX+r+@o}{Qi7BJ+hixn-1oW7;)!4x!IDY+#TzBXkj5k=rD}2YJPo5v+3p}e8Ez*-^^S@ zfHt#VU#2Yv@LAAFGi+a~i-f&{Gx~ZwKc4L&RS-5gXjMyBr0_{zi7rOD@Yk)h>Zr+M z;zwj!;{l{tA-eq=Oxk{-lydo=#j-iQkV>JzMYd{?_LK~imv**V8Gm-W3)GR_P}ek%FZ+$E!l zCch=-T9fuw;VHRKh8Yry;$7^mlKV;}7KhUHN1|Ozu(8x_D!UO8IYN;?gY?zyWu4aa z9X%{n;G08|m|a~NCuK%k22%~;&g+sQM&Z{O;R~|-UdvPC6obMk=$Bp`#<{Z27Bh4f zWd6%L3>n<~TgksWhWgPry%w-0p_!@IzPVq4()VFXqVH?n*}mli3@mLcdsXbp^ic_` zfaLAjnY@t5tV_N*Y|x9eAv2c*+Zi9~iA5DDy5IVRj?}q9OFeGV;F!nBR%XkV{8eV} z&y|uQZ@t+WZQL|%I$CO+DWCPERMMUa-zwggOOu=GC&=34As|wGJhT-+Z?~n}tnlr? z`W5w8`(0VG4`TOx@TYC2<9+KFPw$W2DrkTIaDMCh`QbR|QWzw}W6Am?J8yf%ns6#5 zmWM;NM&5B>E|T@bFWEJMq+20lWJaAW?d*hUnE)3_qqraEd`|HN;U>vEaA*)oQ?ZsNpb`yuFFwpnC<5O4u09&Am5CO3kb} zMli|C$olJ3!`*(W_M6yZXyiGS?b93kR!ZxG_uj18PsMvgH)b=-mM*wf3xx^c-cLjW zu|1wlQ+IFln{1fzXP~XK7j(w-&|1^# z$nw_PuUW#NLIBruo9=s!>0=fsWAbrOq4RFbm9}fY?x6M;g92$&zBJMG7QFi*`=H1f zP!_w9Y!`+~;mgHVh@&Pil~-~XC_KurAE%knx%y`Um#PbuE?+M4B9_bZmzrvX(~*Fa z%$tk`J+KjTAtRAVD3J3Q)lzuiW8kEB{sU$!h}mCTZG30Nl|2tTl*py54QH1hx8KZY z-QAd1$!N37wiu9w(a<;=!y2)n1bu^)wo3VJ!D*^@NcBOQWYrvIb8w!~q8RcKVdGbXCT& zECH4jHe(0Ly0tg7`^<86mv!Tt)q)5ZKu{8m3&P9$3Bt=Lj2(LHaz6E@0S{9C=)K~N z!U(&qh;&%_AmI0;4|K>$!t9e(8(aq4ZANQ>?Rp!Db*)g8J+hx_uN>bea)Rclfo6~j z%w5%z9WZcO30#NwSLi(RS%;7bZmU1{#IE9ClE8LuaxMO@Uy|kp*I6{OM?T2Z#}ULV zKw!flf#lPQve|5Q8TQwO>GI|LOxzjCd4h3`!Be$h!~(spSDAD`dnX(z=1wc_OqRm4 zU*-GyU9j=go`_mTpFPyex{rgqg5GM*oGbSMz3oeuaSLNh%A0K?WPhIG%v@CvB7R}_ z-Qb2W|AKb63=Y9RvaAuk7}K-nkJtjaxm-l7N31jPwuTJkVFN3hp5^eAY2!7XA^Fqb zRSUc5{(ZiH*GYG^Vc+s}x_ECmr-aPM8T2e?`Lec~ypb9)()R7Nb7ot<_AdYZm&7i?iK@P`mBvO!^}-cvVUTjb)If{^?iaYf&=I| zz>%D~_t`7oWj$b}Bom|>oZGHzMi>cpAPcUi2jN3s5t^Ez=t#`g%)Kq=aQ2aP%DZy?DM*gF z;vr7{N_Jz&wv{r*_xu@ttN+<&tiA$_jW~|Rzl;IPz(D2ulN=2jhb(U|vV#2|EQkYs z?Uys!EdH-sO)hJJ{?y{*+bpg38Zxvq7YlXA(O~=ST@DAYwbKfF)?Wm;WWLS@?8a<3?L|>&^U9#y?*rKAH-Mw?=D}QbBuBP1 zz^`T3eM)%+JD9|Rdne2oqX$e528xxM((ow`PQ7mg@Dcy=8~h$7oh77#0B2~+u9Y&e zJvjqNubCs9+~9nyAFrkGXayVSQvwXfLO!9~S;0_F61uF8B z|3S@58vFdM#ht^fs9s_Oe}z#B2=8qg!1G5r5}j82CAq#K)Dx~5b7G{hz>NvNh*6Ffj0T?h!S+~EqQL++MCQ{^FGeZ` zJQMOW;J`q53EVB3pb<7D8YS?IREU)TF~A47O9av?Bn*UD;8}Ebas;ffAn+An(ei#G z(eiywUCZNqq?8X}{Oc^2G2X<)OY;&K>Y4vrqZX?lldI6|^KUB^AsBIC;J=AioaYY2 z10%7uoE|vEq5|@~~&sxbIb2`*&pKFYWjmPzmqe6fYn6Jg&vW2<*=`=u$pfL|QI2 z^5tKCwKoQi?;g$~pCGw+k5$h9)YND)u)=)07<;Wik6)cfWqZw>Sji-2Wd0nCn#0b( z-y@uXzqCtecmccGbKD9%MCsbm5hdNkZghTsp!5|ZYrm`)11nmZ8ejwuok#40LR^E^ zoSDktyQ6d)65c?etv2gDS1t(oC$Mp<-$p|Lh+bBc!w+~~1)VBWsnRhDR)CI|iEXv7 zRAqu@Y=JCJw}g%pcM!`-8~=k<=>e||^^OyEe~gS9pB#tYqqMusU^raUCbSlQBdg2Q z@iFbpG@gfkGseAmGf8S!arau#7l^~g>8czj)q0a8edLK62(=h20JxZsZ?)z@IKr5V8L#gi}e&{(qA>S>C&l252To$^H(1W-zT1JjT~xZ#)ylqJi~>`w430bM5~PkN(k7z zIz@qGKKFj}0((XQ06OMoN`7(lv;?iIYlZI@aOi(gq+R^=y-1uR@pBaB0a#K`@42QE ziuXo42+!PyQx%U7Zjyyk_+8~O10^D}gr|=UZ!b;iL`lpnTI{LVQ4W|9De-*e#*&gz zBaW{F*Vfaswgv%Az%zICz$9ui<5X+@_u!Wk$sLibF&8L`Hu13`tNmQTu zOh0Nt{KNIUsnl7%`mJy0P*Qo#-tB(*<=M>qr3%4mCQycj4=_FRHAul-l!+q9qJ{!p^@>N2a1>HG#j@IENzd> z>?R&R7JeUJ>P%OQi@q0xK^G)V-X@q?CSUn_`;C0&m4{DA_KuC$@p&NnYjcyQlQ}M< z`4Z?Yy~?Zu50eut$p|Iyv8-`e)J;4Sf36SlWhDW8I{aVxj}fF=osZ_VWs?_nC8;?=Kbr)Y?U?plGfC1oLA8%R z$gQ9P?gl`5z6a=YTen%iyrqaf{(W`3|FjqQ*)sKc9|CJ2uJ`a=+Ks)BO2adXs^zyy zP(oc4jdYCdIt@3yH4t^@E?)^=M+A^liwFU3t=-llw7q}PgomZN&o!8ZX_xW~hoEP% zwB3Hy@jl$lUdCN5)f=8gY$WD+5!kO@y{(p|Q{GGpU0fOl=CK)|P7Zg2dZw^lhT*xM z$W(v(qp)9n0Kq;}h&qad7UIF-#V`V=Che9Hz{OwNC+5gN`Jjuzfol9&j8quWa&9dJ z`As46?)Vbgdrjfew$&SUhgq>3*CSq0f|U#eiQB(YFus`}E$^`Wrd>;hndNj|@>9n2 zMG*>yql4i^?$L0`Os9z9DYfcHhXS(R zE4g#hMhwQyz+qL zKezxSaEmo+W8Vak%eCH*L_UoGP0A5PYUOp|`!p{g27nG~sXxF6f~npx*L;D?VGxPv zM@iNM!2jWRwkrm@JXq_viKI;eGwk0nC?VLVN8gaK-ki36avl7@Gk6*7Jm3tF3FW|s zxB(bd%FUr9`$?{o2>qA)JdS(uA7?g7VMClXj&BO_2XBqq9zjT0G;{+=FZu}jG|wKc zXspH_Mv^G4hFlymg5SX0T@eK<=bhdlzz+)V3dFE9An5&3T@l#%Wqalc* zoqGzQLZjd{n9RRGL@`xml!59T^6hI6!gBO{eL_c1+8fEIB5$riUZwhq$tJ2KBdH__ z@Cv^b7&%Bp3;_#gAxew({55CnB%R^jOWlTxatD(7;hYk6Iq=r&1%(@Htexz#c`vE2 zK*bq@KxfsCT0zOJhy4$>tUP&imnzV**Y2Nlb&G~| z*DGD(Zk-Dj(eYXyI7@oX~#vv4m!>%D9#z z-Fmh`9YVmTlgOm0zxfQPdp5ZMluav%^_9TGeX-Q-20KyUEpbB|*b6>Tm<@lx-aD`fmGo9>#3?BKo@ajEeJ8F~HSZP=%44O11<^QL$ z2ico7&*OU(i2zx|af9p+Tm}`OQsG3Fw2(Ua>0S!e5QeVoDXZYcbvIE@mjdEnMC1+E zy{_jANd1PWVYd&wa>Zy>jf3&7JVd7;eieDQ8b*qQP}ACYVFHzYCPnwj_*4njNgUeG zomLHLq!5SCIt4}?H)jd+cA(391$ytVVHOMAfhF7{XCz}w%&m-$sW;?w_e;Zbjo{hU zJ6}`~o=T!o9RREIu0wz8K3^+Ax zF2DDIo+d;*6890Z86m!~R6e-7u7lbiDM1ik#N40B99HkZ0K5dYh(~CZQht*VBo51u z&OCIQh!E|H2!yFBC@Rb*OoFh*)Kt^1=0q1!FrA!3Q9~V`x7Euixd>fa!OqeeCcXsC z-8w4`b&2R;fSZ`3@(scG@)^C}_FcD}L(TfZI<9%%FD4?Mn(94v&b8I08jGaJ@kN`5 zi^KZr1MP8AsWTnd`^q3&^MP>-iL@)OsDU6LGR4rB z{ua`0PL2?-n6T;YFEl$-Ri-ndTjt5>W>TP`?D2p_(@I0zi7l2^yR_dFCz zX>x0GY%%=7@>+DRKafq5gGAnHiN*Vnw+!o51`b9zw0G_?V(a(6%tOeiwvb(GcOXXhv+Q^;awAn<8!PO3kcfI)*Xg4gjp*)g;BG<)`aTCmHB7pEL~7EJU!G&n zd?El^*iOyN1MOADs>FPz;sFe5A{la$U0B zIG3uUEi)bSrNt3lrwdIX+}Jvg71(VmJ30Rj$}A8SvCnS6If zfT*4o-;#trE!*A*z=gqD%#_t=!0qa|fXN)h}TiGu!GK3iM640U&URDRSA%`k%hD=>pzD&|RP%RHH`x=d2S#9k5+Q z^21b0GYr5JKIxdFqxl=eJ7n87U9^mDWBg8nwGH#zaj&!{>`Z>Zcfop7?jiW~yQc&$E#~5J>>b7G30(C>38$$#;tDr3b8c6gh1~`Pq%>VNDjpc zsFl(|RbQGix*73caW}<(G?PiQS&wq#mY_uNjkiT~^ZYhdP-VQ!$K*h9(h!7e_g347 z5m&{A$E^)tcHr;{;QW%G4+c|x&_2(fd?z2QwU+hM5x7}Sv0b1yJL?0DsWjYtgvq2N2O4C)*)l$ddtK1 z?z!p_3qxr+-G*oNw|bNZ^A?%3rSi;;5jSNaY0+OV;=i;3Q33v2*R+EBZ-rzift^4m zd}p3>`$8rZdegC<8Te5tkwP)`(~PSIP?6&(sww5=lSo= zo$w(bHvllJuwDUOY#Z?dBtO0H$?3|#_@7-Hgx#AxahvFGJRxuWDz_!GjSyz8(C&4w z^`|fJ)Sqi2pFP*Zx`kmI_mPEs`|T@*l5gllEy{Fob)FS1{PnLqbxDli6LL+-ZantB zcv{0}=lc$1$o}vs^ER3{`@}zc<+c0!QC>W0^rmfjbRuuN*nr{t=+lE}N|95&*V(SA zgpg@@M$evUaU!(Hjkt2{#M`nv_|IA2JsfbTQ)_lF>Z?vP9u*X@lxCn-ZZ7fUrjO_g znX5l;yA!T5jasJE;7!H265)cx!vW`P7f)F(h+iLkQmRb~*wJDbm07u1DRw){oj$v2 zdft&>!|-zLc*`6{#R(wHdI{dnG{`29zy0hz1|k%P^aZJIy|-}(=FISbwZCchTI$6+`jG<*8bjS2 z%^2F|!6}QQsy4O8lDM)~$QI;J;okgwBTJ`NC_jDh2c!!#Mpk#dP@%WI%E}EylfcWW zk(@wU8Wq)75J1j@kt_i6Z|N8I=vN-FTS*TiBk<%2a{Hj`(${aTXY=F8{EutL1rH6m z5d)BY1;;qc+O%5~wY<+CA~xN8Kpdyih}%&9`qmB?AHKIy+b&RRZDSx3wp7*2WqlTJ z*2p+5_YGBH9f2NAEsXr9IfekH7WvypgVHhNC;RIXl(bfzEqckIV_tO;w!$#5%SQD{ zEi5h0)s0c@vZcMs0j3AFLou_mgi%C{^S1BAxaU2aalx5G_y-ga4=+BDNdfjAjw z{*?z@&V34fkE0Mn86^7F?dEXgYa_auqINkTiVvcE)4kCu@B5UEMUOpfydz}aSDlhX z31gf!(Pg|=lx^6;e>l<<`iLqJd=TPvpEtHu&=4r$L?_8t*N&u-$e1}`3zYS8SWgNv z2wN}Ibx9c{!{(;GU__m_)4Zg2k|ROJ>M!qF9=R6+#~mNokQHJ^_!N136HRI@ir?&n zX-XnR#$}3WoQdZ@;N7wvO$Xm%N#GZDk~jrM5PtkxC>>TrXol-|MymCuS->d(aLyM) z-x%!t(MH$mt_31LEM#nOm35VO;9*=*gR{-Zhij+eA;^v zD$TUJSkP3vFeFnSm0aOgdQ6ZASG)yXG8Szj9$EU1J-B?38Wa;E3giOcTSV3amF-;u zFYn8PI&=8wRuTyYouKvBG@v&}+vn;RhtAc;?d@dNJwP!{6;QIKwSx4^`w7Czv6&3_ z^h3)p693NEA}t%i53%&E03H+WXx2>VcN5nDpa|Z~iZ=Od7hb}*J8$DSV{G{*I3~|{ zaFWHtq{AM80L6(G86ChmJ1L*kARCfHZ*X;O8-!~oYyyO7_+P;{o2%P%06!==L-ZEt z<1#qq8IG$8latO;xq$ZT)P1FW%9(dkGk0UkndR=Cbsta^<%UJZ*{u{XoP7HA#)w>**{>!FyoU|R1Js@pX|CF|u=TGGe_eS5! zIyJ;dT`}K#SGzuq`a%O{nD+FB*AHo3|anC;~{wRc8c9S`3h!pVgv*;(^a(c?NVz(ZxPX)>(qK%PFVimWU z2bST-cCLhq%4hNuB=D=Mv~ZClS{<7ro9KmbhGRw*Am>gwi~1YFDeT#;8-oXx0nWQ; zZR4jIB5Q|i_=m%>WXdR#iP~RTVsu}BjJM=0tc!mMYAlR~$DJ+Q?80ybO%D2{jQ<$- zM5X@iy{8L&ZUfG|s}dN$-gweEjZt+jBQ2pKZ_RVjl$KK1)~3|%?c<+Ct168pnl#K3 zPL9%eRgG!Jm6csj+r_bAp(~Q3VQIP;POqjUFAZ1am(yj{Zbb*Ny;0#AqB#_>k$XM_eWO{az*F0t##>@Cq`ee+S*{69 z%AXlx5rZ^?4L2@vU#dc(5c?kmJ3$^c{#%kjD6VKC(OtT!^=ri5g3$ga;BpTxtJs3!7#>yTU8oyN@tcWQO2+r|Ea|%wu8cmU z6k0KE3_=F*QjP^5QYl28hOmE@D#5_TswViYZOHcC)v}$PF?g18wBM=%wQo$S`B@AF zGMXLkvzmDu{%-{;8D%@y@Th(CQjA&^vOko@wy>L*Y)+#h2U^1voB}nc8sZg`-P$$Q z@fe(u>(Y#2KL|k~cP*0Es_x#4UxGUI=?bE4?+!oa>vh~G<}R6Lc^6G;8{1wa`s zoTzCULOBvh^%H(c?EQxAxNy5%DH9ORh6 z;;2y$d7x}-e0}84g3soVKl+y8kh5q6vqGv;L%lrx^irxAFSAlR%T33`RdMhZLD_H$WKf5i(aO z7rxB6#|2)W_Zn~6dorJ6d)(IJu$s?J(*wm%pjV=J2<(e$=jOAP=TR7u;6_QvidSb8VHHECrzU5Wf6FzO}rOFNh4>#xcc0q5Q`>vMLZH zecG3H7CmQnrq$FcpW?lTYhRVf0+|ab+G1H8N0D^$jU3GF%?L9Y$!Hb^=x=A`0h-+j zox{;O&mS8FAq=$(+siY=H6Woy>1&Z(QBx zD{nM%KM!+cbA{BZOC_9ECs`+9&GbQ}C_Z@<$BRktF5Q|4Ez6N;nRIzVB{5ZZ7~{O= zn2D6#*X;PTL(pfPhYeF~tL=CoPCFhHm*!~Gyd7|!j~9XOyp|OEXb&(dfirH<&`J6r z5wx_&P~+Hv&Rm2@elSN=Ry#f{sk|RTu?;H`y<#+IKk^{UnC6;C7cL@4i#Rv_Ys%B< zw=J(je;z`J_>EvJe@mYM4bB(CrdP~z4^lL~HdGZSskwyn&h~T30_NGxzvaOPgaWIE z#mGa6rO|g9HMNTaoIiC{2N@XrAGN|#9;_FCW~u}P#P*mK$eA?~SD%kQ+2;^psi|J5 ze7#;9DtEly_tcHy0Z2x+5rD3fpNYRsj4DueyL{hdY1nB7@U)3MW*ay59pt4hYRYsJ zw`|9J>-)!qiKL_an1KmdzSZRBHr)rOg9{^0b>JYLaL8b6g*LcWm(FFc za*OIR!C#yC_Bit0mdkXe>K&8m;a4WvCf@Xlr8P9QaACE&VbC@Xw2hvLBYX;3a%j(P_C$EN!E0q(=g)Ohz zJg1)p4xt{e;NK|Y6o?Kw4#v_1{)LSIxWk_rQ!sJHt<-Ze6(Iz32OkKdhy<7kP({Qn z{lbbFHR}|lMEXsn{dE!Azt;nOk<`=1E4cJ8_iE38OkSz8SPgvW3nq<4qM5=!FTBoY{ajHzq`>x- zKrnx$J=Sm2jzMn~JADNeTA(SO%K%XgKA(wSWT%J6PtG@l+#Ucs32=zLY(%ZCHh80zO>$~CM*D4QIW+`D&p-YjrA<{&| z*tBB^>`yv&rY|bFyKBfBTpEncqXVkHa}!9>-y-{IA2ZTjf(ED}7`7_4ZkovOve zy5!71827JZZ$1L_oK&KC!_9dL#6rtfFZTiBhq)i8{K5mQY8|x;QA&fXI)wgAQ64t+ z4{PwmJu=-ww$oVzRvWCF#RAUaVZLqRX`I_b=a`8+@ud1>SfNE!Q&37U;^KS@1XNLn z=P$D8=4b&n_@vi7X^bR{jxoKbci5MRNh32aL@Z8fj+oBx2~@oWzA@mPLvn@?d6g`E zJ_Gt)5dEomcj-`%yLmM)y^&(-{@hWT0v<+9u}3na$OeHy!}@GJinxBZu!{6-?a2r1 zUR$T&c995>KnxNLvZ*GG5^b!97%~-yL0tW~f`l)A#QoSUNxnY?LX6~$0WFX7L)^+}$dtctAOT?E+obZ>9+QaSDh)5x5QfKW7Q3AfPh4sJ~>s-9q-Xz-pgT1!^ zs;leXJOhCQ3lKB}cZUGM9fCUqy|@$H-2(&&E;o2^cXxLS?#{*Coj&CKzJK>rcg;*q zO;t}#eMLc?f`WbTK8L;cTI*TA2LpjI=aMl+7pATEt9W$`>f3I4(C<0~RigF*u#PL6 z+@iPl`3K3!JW8zw+*qjt?90YBpeSI}*)!a(?RWcEML`GGW!yqG`L4%)Abxl6`C=f> z{6MJ}u+j=e?)D(le=Vd56LaBt|888Q{QbMpDP5!8aZJTRS@e8RYeJRTeI1fYJkjZa zf1BGmkkFbM?b6*hY}pj{d1>0Y^;kg9&20zZ46^1fm}oz=q3>fU$=TcApjx*#J(_#I z)v!NZHsb&qLvb;CqI_Zm%KpZnke*BS?du?u_V+H<7cQ=M3D~c_;u)`tz;9H~r!IGN zFBScYS!!;}tk`yj*BaEeDHlMgr4CyvRZrv@Y0&=%RIb_&x zBP`j=IqwD0*~UI`%RXUkQX`R9&18?_Tw|O4X2ru<7HXLqoU>W z(WA3wF-phdS!6l@NOUKNRCQ}+@P0ll*F4bLPH}cCdh-h*!ikTLFJYs}t9jlj+Og^B zZXX{}(Z*z#;?uk3qp`Pf?!(cxH9J*i=PHLZ=vJRSskJxm0FZH6k?g$XX)XqW^Q;wg zZE+3;wc;$#)S6HLPpdF8-bu5qu5X2#nG}u@Bs97TIBY$fZ@Zk22Ic$03vgPE$hGeL zTpoaM3Bm$LN0JG?dvU%yo1d4yc3Jc^LDe;FNz^kK)vQ7`1GG%?$LFf89{%$& zD^&}=2=Pb(>m3SU0IyD+T)XAd$pz__8rc_|`-7oYsaX{JeqoxLZc551W_=)p2!j}1 zxaNR&w{FOD>YR3;BAm+8E>m&!Ae&CNp}#?^7cy46SRClOt;1Q6km;>91bRG5@w)e0Hk1$P|p6t3dj@kPtnqQk!3NEi?8P7iAZ-1qrpp)HPxHp5Hge`N|)M{nvAzpUBFku74>O=>V`!gf?k85wa-2Lwy z>71vz)-_hI779a)zre!|0ili3k+o|YYg7zHm{|Ezqo&0uJFSQ~h=vM#^$9~Wl2Hn4 z^xSBs#JkJky~&KffkgJ_T?&`AYFGq9m%w^>I`=tfDGV$9hGhS4=84CAkXH{m50LVI z{>cgahv|L$%O9Um={)7H0uVr6hzXJqW6_Ba%vAR$nCI(IOj;UHtyN_O;AEpzVRCL! zR6Zz_-$75XOC2Yo3#w>{t)3WK$x)5v9+ihnB`yQtZ_A7PK_G zUn8cjp;`>#p7h8kWNrOk{>b%7kd9srdp+$3Aad_H#VXfX{$%B1u`Sh6M{DsB{yVF0 zV;q>oV-em9?(J1en0$hEx?gtpZhD{8 z#gH1e*&)(p4>ncVD~2Jz5uq9JShT>jvoAnLT4L3zFFLrI6F z^P0^}v(Yhi!pR2Kv9P>W3;NQ2F&g(G?MFTh z%%Y$6#dO=m9F`(Yh!aJ_4?Uk3bPLcC#8D~G?Ub}Z)PVGCF^Xn_VERka^K9YRdyh-o z)ADzA*AGIOoW(r4DxvoG1JRKyvFjVn*DiFT!%u6Jv;^LXtd>Qc#=p(CWzTvE5vw~8 zi$Z4OMS$=rmySgx>Td6&jXFLdr(M_iI;`sI4Ge9g)APojC@w?^H@?@en6c(orAVzf zwCyJ+`7V$nzioZ{QXTxn`pGv_EMiwW00Fi&K>}ORcM#0DZ-TgfzCCI}(HJOIu$@~B z=epUaFvH_A66oK?JfmYi(=Rt*xAhV7H1}09*z*1iVqbeKDXQi%QRpXWH06sx6g3cLomnRub;-w$rwGn*=O$Jq z@H?iCn$f3hAQZ|{$aFiixK34ZVB=4EHDm|Z2|?3TOHv68YMPTnz?qI~9=zdVRL`h0 zBA=5D@*`gsVY>d_@4>^K>RFOoLG;MP9yb^1g7H!cMfU$rj5>20{oU-HIr&Q=0QbH7 zwVQ$pwVdLW@s|$_ra;;K?T3<9PMeeOFq-gPmw?)WT{Sse{N-C@`!Vl3-v%{3ubnNk zpQSLl6=#T?yVD6nr618X;d=2|Hk8x|--|hL$hTSO{BG(e=grVG=`HE>2b^rg4Q!|FXvGwo97a#sO^`N1D)=LZ=lC$*u8>UIdd(nF}a_SEs40^qE-{OlYwU5;t z{6I2|IXps>Bn9GpyVy#(6Ox4#oEla0ZzKn+*CiEHSjluhDtCaILA?6M~3aVvp=R~<%LkrP?5|9X=JFR zKdmS1Me5e5lns1o$RkRI>vC)%OTY68Gerlo@_?7`s4aZc!c>W}uv^PTa?^Zn?gl^|a}-B>`R`FB`WjBWZaL^%3)z}R zU!nxj?fbUljJ=*7HxThSS^Fh>%$rU!)EI%hzv91sEGA8F`|Xa|8+17EbxU_BxWCB! z{JmQ4&Un4&1NXM#YdW2IG+gsY7*_@HKT~KwreU+AF}tZJ@nn=>V?!MWShEI8jj6@? zB97YyOjffIR1CC78-HTHyB}sg%`H4SIe0*CP>#`e}L5-cBpmOx_nKgtBMm{V?O?9?LBNT9u7@VKVF9hA20LwqPJT z1`@qyU;3&j+RXCxsJ^yZ8CnwSg`Y5?{00pOB@OE({GplWq0EbZ@!UdYRxaa?8n0!U zv_$s5oBIN3ojy?K8x149_b`oL!F1{+#SugcnnsRxgYlNkkO}x`8f29Z6S;DwjELET zl5u{jRCJk(KghC&RILVyZ+tB@HYEqx$hXbyn_ye+0h|vGQ;;__PHeXxXmyX1A(}5` z`vhvwFE1*V2|&3% zb*omz8F_oQpYui@EO-l;^e0ni0<=-T{K1D}0=n%B9G+|Rl5$YqJLPuCX%3o+Hz5A= zza|YH@LeV?WGypSssy94*pp4e_DgAKYLa}OcR%Opz@z1JlMiXB^yVtwQ@d5vO0AKA zGIne`uT^5>%^lBIC^dhtv*6wr@}h=v$*p;@9Cmbi1I0ePHxRGmS+70Qhb>}Jba_Z@ zj_VFe5P87G(t|Ww9`7_8v``GD%ydT{!(v%A`&&sbLZOw&=hVOUteSsInKT{F2*TaLQqNt~wUtG#t*}-huy*Jbnr5HN)Ca1+b zNs^w}Rs(4Yv>ig~=M^-FkQvjM;)R+)mL<0xtM=-BApL18ox1-8EFjxSvUy$U6$^HA zxHg|MIbv+x)hI&+Qtqadud)R}R>f+|$Xwwz@4q$Nem~PA>Mvdd0X&DVjqpoNuAz&2 zMp2&t7oP!qCzf`7EEt5yVnWlkvYN3h37b$2`Ar`Q8xJBt{QC7!Hz9M;OGx(;O+~nA z$yqHJ>dA0$tDGvH-&=bS)5V8GFA51_?e13qB->%Lc?huHMXZYg-IOi$|9*DWD_qzL z50{&Jy^RU;UHIC+#aUA0U19)e39+NXu_QL@sa^F-msDIXW+Z4_HVZo{!4^R0Vg_V9 zjoxu>aRZnV|EShd^|&jr^9x(tuI8YHefe-Ylghm^+Lvfnuj)LOA}s z`f&soNf%Vza1Hsduxcl5g3Oq|f?Ef#%FbNX_95_hIrBL*G;zNFVvD~*(Dat#TY2AP{vTmO!c$I zZueCPe!MxgN-OyeW&7XX1-9;+$q_W$NMxyvkoIUBSYP) z8XUSO7@-nLGa#7>w!$Q9eB6CQrVh{I_%Ul^M{ond`D`6cU^ zk9flSPu9=u*gp5n%&zU$;|o`OD&wm=m$;_0Q_21F$MHIPZ7zm9*(Wt;c@3lo8E<38 zmHiZvv}0a3S!vqH0K1$CGb>*??#QvZMU}78LiV7;SIH;jfF_++&-wJ!UWT*luWA@?uH{+Kk9SbY8zhkodCZaS?z zgo0}UbV2P_s87BZZ9KuN$rg_ia{6=3X`pBJhRAR~~ra8jcNm zWFu*&_B7JigMOZX9@ohSFTQ(K=7f>kQ#|7aW7BEUm0o%Z&%kG2g$rs>mm$8ScdgRQ zl*eVS`3Mw7ok3PjW1O{Sz30DpfQ|v%Ch`btxA9Bn+1uzWbK+8I=rNJsvdzYK>`~3Z z%c>}&=~-ldnd8F-|9@qUi>!R7TSRqI zkc{LI(p9(GJZX<+D-Mcb9ky^8do6nxCw?tONf1Vf$L78pO!JGHoIYx#tEYR z;DcU>Qzl=SR}Nb!xH}+Pq%lC6XBG~N6VTUC6ob=l>|Ns;BDhIdvV!@KIiFxNK-zo8Q@j#Z8oDRjDV z-T%=ejZ=j1d0M^sJ;@EUCvYK*rX0-q*&pX425_wIFL&O>(~BTrSNJo9BVy5ozu#um z-(wvdv2xuS)yXSnA#I*MWL>OKx4l_D z4Fyvn&!Aw!eQV3Rl&N)m56gyd1lAJ4Px|JQ3OYVDg@|(w3(%7B^7O$p7 zEN-o~P}WE9n4W?R!u#TNctlZNG^w>aw`@Vya(*d{@GAsmMr5e!Fd?`EjH@EU z;I?c=w#->*aPo4;CA&2V(6Nu=tD6Qi&j3}VZ4i2EWGSa!vngI z6;&PNJ*8?%@+HR)a212FiTv#B!5x3u;|{#O(94I2vHYCbVgT!fulLc_U|s{~{LwfF zB374j#BLN;RBt@@9wEj(JQY(d7DB z1+XnR$mGJbXlQE)rfzl3%GEJPzvRoxLsdDpnGh7xh`X;|`9+@wey#X+&yv>^gyT+Y1jl2;N_nt&CoM|bC_B>AOm{k3m)g92%g zcpwta`eX`4819Z;+4Q-7#4-~_)AmRTrEs7d9adYzE6r^MaiTtQ$kL%L6 z=c6Ly7bEeCxmHRz_fJ8_(U^f9eG{WiRF}BRfCitJJ_s2D+7r z5^;-&T9p-64%HhjHI&)vu9L4QvJ?w+%OQSn863~1%@P|`ZO(j5-EeO2ilFH2^EYug zR(-qUqUOw~-IT}YjJV3#a5H~CD6Lax8vM(Om!R&sMeR|=dTgCQ3!kn{?qNsCcc0ig zfyXFuD3P~!{^pZ5_1M#SguVU8B5xw59!SBK7|8j zX5;;3ri+;-gv~fAlzDB$5a_e1nKv4^mj^eE$5!8fN}=dKk@DCKuSDa=q}ymRODaY{ zNsq_aHa^e>3rUA#TDr6A^AzgjYEo0qaf3*>(9q5lSc7I*=qL2Zizua@f z+XnyXTnAt2XbK}{^-_!jzbH0IVw8AHOZBF2X{-Kj+NvQRWv!~+Khqjh&MXuVgd_~c z7Jl;}D&^Hs-cvnc3n*d1_2n|MAK;xjYn%t5B~Exb5)F*z1B%Qxz4vDI<&A53Lwgr}6i&xW8P{N{LYWl}EbLH>;EMGc%N1f=_NS6!R$?@)B)8|%B*(>>nUpZxzO0#? zdi_=T2)$z*hESn zsTN_h#azW6vz4*#{&XwAJa2IP*0VEd(9=S4?0urp#zQWJp%ky?1}hx|Q?`10N;z0m zC2tp0aqz_mG83yxDKj-X0;=3rZ|%%!ykd+mMXmeeulwmJGXmVOL|R!Sj#SwkT-NN$ zpI~9VEG_ij|Je@3@mne8Hs8YuIv`$&sSs6@vV#nmJz{r}d{A1Yna8-$>C|>np&xPW z33(D$&-~`*T0G9 z0ccdMgPkc4@oTQAGp2m#zYYXj6@ezd>K&T2f@6ZI-iNKNb-e9Th#nKJWNb~Lb(50E z)tue#g)X-z7Ztc;JM*jK<{&JID z<_3WF;B=1=k7qw@`~3XCIl@dK-#mY4r%eB$%bU!;g^s46tchp&`T=iyMF2=~EyL)K z1B*-8xpk@>>h=mxaMy|#S?hK8igGAGmeRr20cvB^=&#Kqxkz7!!=NRTkd>@ICBkl2 z*rg$vN9NpAoLx&s(5&Z6n{J&Wtz=w0_3$dH-!+%3&>j#EPd}P0MziHbf*7cr7s^a` z$S^|TDi4qRYRc{>#mk|U}dUGb24AMoN=k&rR-uax^K2j>3{h6C+R zaJg)l3luVs*DL&siXNKQD10Xk+Q@_ZmI_eOkCF1|%gA=gY!a}(NhHzK54ki2wjHt- z8N4B_>&+Rw(mf;l{@G=W8CH^|#!;MQnFVR4R5m11d?x48GnxQ>zEcb|_lYxDLQKw= zyYqBIR5B3h=xCC6!Zr;CjSEVRutE!@o3T>m2XV0cHJSK(UNo)cH9$qr0;uRS02N)H zqWruw>v<4e8_k}%7}66koMXJ%}4AW^=93U1s)>^s072}J@aN;Ub`F;KHVs8__j%@rwivNJCIzTEF z#43O3AnX6re?B%Y9e+UT>0ozpz<1CX1g(WWo~?pKFXmKYO6}J1t~OWTw#*5KwS1~j zqc4u=&>k`zFT3?Fk8~*sD@GC^q)W`xm?s{;E7(_pM{`vqb8a<}g>+FMV|_|9RiD@Q z*&d9k*P=Mu^~>ESlOz|v|F~g*c-|gE5sqnE-8m3JU4}P#n~oeGz)T~UX!t?42xbUA zOPA6~H{G_KyV7v;ggAcCO~>MQ)}LmO$?b{%ZFmT|?5(tvxM`XrP)-o=bK*8Q+%2MDhBPnIGnu@Fy%KzV4{Gh-VdR1Jd{>0hT6<#Iihz7`n80lk`1J0a?rjV- zV7m|tp0wsK!gfR0(+j=jHtU^R_$rzfDJmn`CF*xu+|A19vMpn)?`L15j5WQI$V$E= zB3Ro;&-|@Res}MrG5qW|>T9nSEL!3)`W?cp7TzU|-@3gC3pEASF89S|igogWe?jS~ zkEm#C+P_hHGjc;PGqHDfhuER>I?*593WHNs$)${sou}Q8%t|o0LN)T+|Jbj+Nu{08 zuzRMwbD_5M0|?dYY*S+F-_)La<}%rh>#0gjA{eZC(dUsV=3W4(@G$}w3yeJ~Tcb(j z${uum--?zIm%Xh^Mpy1{a$pNPUBtSqUA}(R>zzmsN5pP|T=V-Gh>AuF(7~5Amp6Bx zjY*u$>9c4@uQDcLztBuxZD5m%@sut{-E%#ebT*pyB3l}6;g(8F7me8Gr-E%+wpPZo zf^Rd#N8RkwV65Kw&R$q2dv81>-CEf3wPi)Qbd-A@hr;{D<|l8b4l`phKB*o515bBL z^Q}X#6Tuvtpc7S>bm?3~F>*P-@>j7HLD-6{%74A`pgZa*STD}!a7M_Py{O+Qt~(-~ z537PPLACFd9w{|&w~3r><8txDV;!)jY0nrk~90k zLcJ~?(2j_QAIw3fK`8(@o$etJ=yUlJ4uI3aj9T$XI4t4}f5GX_WsX108xXM>B>hF5 zLEXA%wtM@Gz14k@kY1J;HcJg(WoF9yEH&*02MK1Akxhr$5blhkMuEBM2}$rM5-~^H z)fRHp;c&M<@JaGDy{?8`=h7#1t#j}Cbt_XDYO)S>Rqur}Osn2oWP*5>+|d_sy8U+S zV!eHL9iEQk3pjm6=|NA5k0N#wNeso= zcoD(Bz>A2sRPytkSdYyCA)*fJ6Xiw<~ak=sPQJ2k3DdnOGhlNdFFiDBF4W3a!(LPS{RxRO>#n!DIY8fonfO#+OM-|2W_^D7Aq6+p#p!t2 zJS@R*z@czc(E#7p6&v!9#6Qmyj|$fX{6dCz=MBO33db{6%oUocG)MVJYxno}6Fr1b zAVL%qD$wHGFZqX*rRGg<2Aw#q`*8Ir_DqIvsk;i5Z$?;FjYD8i12H@GT|H?fyDrgw z%d3}^9mB`#4vO)vZOwrTw{-{mWBIZTC67Uz#Itsh3_*$?yD5{S71cMs4<3leBke8U zRv>@uzYM=<5Dc!hSrdxVmYI%#Sr2QI<9{p~tSLU&{Xz8FCRYnB4Gqx8`_XkszQOK2 z$EF)sPo&-lF=0}PVbG%)dFm(5NTX!O>%z1W|LU^0tHqaox{!i@~kb&6oM3Y4# zIu(gJRpc3Kzs68TODt>VTw$tVH(^K*ktsMF#^$H0TSLc&nouO)I9_68aAY}u({Syi zagkE44yU($y(rY)-QQhj(x7{#P)nms0~_;s+N{E8kmjaK^nqG1gGUD;@~ZxXpXAT^ zhoY%>jnYCN<%I zaWLWFeU)(Kt1z1GrQ)ccJ2=} zQ9I~Nt1+(@lH@)IufJLVWz>Z=!TnEEP2VJGqVXOnt24;C!%J;U06I8UAnOuXR}l~W zdv1OHoEe${Miua62%(X|r#&Z$FXu2H7&LZYAY2*TQD9WVR?m}fFVh4HxgKODvF3Lv zjZTAr8q^WrjrUHxtNz1OoD`Ao-Q}L?UvN5kqz2{kw~A)+W%p2819z##Yp|-=|Ad1^ z<%c2BYa;V1`I-F>zLLxcI_eJ)q0m323XHt%A_T=tLk9-{luh)(lugGM4caSsGAiiW z4T$^Us@|KnG*556`LV6sn|MGSvQq5{yws7Eg`(&_zg8$Keh+X@FXaU=kenegF= zq}2Ccm`r_f-Gv%_ZOOAC}g$yZE8;*wiwg@}@?{{j&45j8tr$M$ykrtOr>YF9ub4YKt?5xcS-$e;U zr=6#Ac@>#VB6pTQP{xc8%eZBJef93xok_69Wx%ya* zLw3+US|FMzlF9R9y9p4INUXcLMgbs5XbbDLtWkSPR^US7F#O!D^JRHWzHmLt5(O^u zmu$7IIe<7SAck)=WfHEenY@~NWd9u{u&ddOZ`a3s1TM0idR?2>$bh4W>>ZD@B9P;V zpFDC|Z(S~<5~A=bY7#<&BlNY$W(YXm}9CnB^zxk zwU?nV`Q=i(UWO{i9T|K_3ye^U6EwdpY%}Jn*=_q*LO%tT-6ii(qD# zJ3`VZBi=&mz3|OH1!c*kFcUBYi2xf_GMD|BpZq6ll@7ph;fDz@{3+6}?ij#`>`d4|Rq)qw3ETo`y`{B@>tCTi;If!bzzBa?{dhh~eybX086a7r-Ar*K zFHVH_vRMnJY)gNG;n0q*JLc$>Lc4yfE)74oW`nnqXcO^yObQjzM=JN0(sX7WU%5xa zKJ0R2rAC{9z<9IjlyH-Usvb_@h&q)}aWZF5-@`MV^lUE}O`qYbOl_Ag(x0c5kU|hm z!t11Pns-p<>^Ft}T+;kvX-2YAWzfBAMNW<*=RX7=KXZ_H6Sn3}fEgksjtZhxj|vq8 zm`%j9H(tY*Xg$ld%(C7yU?_()`0+R(~3FL z)b-7FBKeBh(LiC0zMW+QHc~(nuIS#<-(v18SNTh5V-hD;0DRF@p?4HL=s9*c={fdY z0dG1zCY_o9O)R5%HFZ@sP>JB!GBLb?^Elhxe!y77FNcw9)@GR+iij3d}IpH%$bmyG|_&rzuaIZ+K)3=h~2%6ts2CH z-WAeLhx3^$=%20>Yb?xMPugpu$?b=F-pv#}%JAKp=0DU}`6qKZ&KFq4(yxy)8z6z}ecc4t|FQqrvjw$XV0c@{BOAq8>CsVS%9^`BJf+caJj+Zr2)f7d za9)B&wSiujivm@mA|z5w_L;Qs`KQWGhrbO(!?gHq;MrP(&2Azdr>6uqP8_ZO;`(p* z>!~|ou6NKYR$O?1LDFCx+FrkXI|$>up8=7GI8_a@%Q<`PeywxO5kKh--JM>kw0BVh z#!%EU-=@9$!+}vD59oGG`p5n0##ZLEi3p^Ld2cNwnnq-uwIoFFsP_)}(|a{Ns>O$j zE!F)>QOh*^i&!$b)J5796PPzE?w7lzZ-)*y0D!s4GWW3tqdkxqsz$Muf|dwxn_rpU zH}4^f%hjy2QP9R?5>+XAXg`n4)_qIfn4RXShmQ-O#3l<>dug@EMDVzsG#NC1s6>vY zaMxx@#FSxE+!(muoG9!Q5X+>@v&N#;7e3F{SO#4tg&5pF^Pw7u^sRWWWKgmz^f<3P zii?Q*z!6zkzkEP_ghz=HK!UTFy8~+v*~Y1?7zi+2Na$r-jx5458*EwBb{tzQ3LL81 zg>E1N`JV#-VcyhLB0@EPe0z?aRr}HX_Pq4asoV{bT-1Y4?V-HsrlV4@m9YcVUbS24 z!0Ge~A!?BCdHfE^%gU#!>DuaE{%7q9K&7WsyJ-Qnn9rCxj=vK}Then6Z8-%Zk1D9| zyA_<;9j9pF;4bTpg_b2&EW9CrWF?3FN!??(HT@ZfUoYCDSw9SWxq}4?OoaVPhkqD65{;n zz9dYl9Uxu0ucH=RC(lo=pfejNV&5M44%Sg(NZJWm-0ZL!Cq60GP=^=1rvl)t3jxPV zjXDHJcl_++;%Zn`#RXeK37zzh6M!gyqHy&MsUoC$CRb|VN~>s8XQ7F5kED9JJb$L) z64#*y&=l?1P(cH=Oy_$`tHnAmst}nABOqd11`S=syByP({sgM%04px7<5>h?us_$k z!~)tn3V=r5pAP1V(jnW!R0}AnF%3rck_moM+kr^&STZk_ z^w=TiLL3ODEcgXSuJgpjK#KKr6rZc)Pi5u;vI1%qadz#V%XZkvp^>OmbWTZC8Y75i`%pS9ccm9;r-exs#>9|BTni~ktMYIE2E7^TgooWUjobI}BDu6iQ@I-TwCDB8 zG*Cx)Tn=h)klnOr@Qz&X`Nu%b7DiR;zq4+-v31PfIX5B2DIXPc^Ulg{GrgWE)d35R zw)_Tw+AKfcsF{&L=fogvK=8<%PFH+yA&^|CcWMQb5O5v*ZxL6ZX zZ`I4hVE8Hz*leJK*Q38^zicc?bIZW)4w(Xaxm?gsj6{&1aSors%k_VO;@j4q+tdY9*RTY%d?{555s`W zG0$$4a~vLHaFaXQz(?CAve?fCI@9g`K@MB_rL82jD@TVol9cjL_Ngz>alWk;*;F3# z4FEd6^)Kl7!gooZN03mnefw0>BgiJ1gh?5YFx_rcE8<{QZm&OPvqZG-%rX|Xz97ft z()dVajvI6c8C8n%kXcsKd};yY_yy0$b{Ivn)^5dD9v3C+@AF~c9ePLVG(8Mjm5cqx zFGn)G_z#b$erAPN=t2l;Z_2f32ivBNvL#O~;uUvN;?DXyd>ymG6_Ki82O@27kH~(o zFGZ@-CTQ`wl%y`IR{F%!ME0-GmwV|IO0zsCV>Whyvt?Pdgb=(NF6_0micScbU4E>W))ZrSKk%@0&y`qY!Y`Htu9IBjJkBV}fK{w9+%!9{Re>>!va{8ND zR9~uOk&|9TjEej2Zq_WN=9KCAYR+VE34k5ZLHtGRyC+EFlsrF(K}WqjD_P44bu*!l zphcX(%J1R;9=RI=sHvIV4ruRer0>LZJey6LcxC-!zUC{c|NbHYA1;tl2IsoIXvFz@ zk$_NH(rFjy%hCjD{85WIQ)%6+L@+UX>k#vb;~iMX@IkxFyVmH9=}AQ*F}HpI+W2YC zF;Qu|R1FJvRJ-Zh6bm50jV4w4=DCQ3$LlG4I>#5iI*@gKL2$L z3teGr7g5z=-eYcD;>_@g7>~nN0XgNNcR*EM3#D$(Xx~rhLmMwMd2WO``@{`m>70`$ zVaZpr&_!kP(&gIM{TTj&mIIQZ!(xY2b55&8pBtCBi!C|FWjVyDmATZz>5^&v9rAK9 zNLm*RD6283&W&XXuhHFf2jg2W$Q@kkGC$Hed&TPpA7zg*g5n9%cU(@>yvejQtdY0c zTvvMCB-4dp9d4ao@gKcT-J`Ure%Jo$3D96?yV!oKUHDli^8n-Z)Xi!JF=MisIa>NC zP-z?nNXR0WuaR{1_o7Nr%)pqSnuq)t-GDsaKha#u1wQ`RGbLIv0CIfi;WiG9aK136 z?PDKs`hx&L&a@#Ym8YIOsDfvmOzqN5OC8+o(mpkc&tW4EXleDy0A!Gd1B(_sS!-7g*aS`6xk=jL|nDG2S@W40@O5DH;61rZ{(^JZDz4w-JyN_J<6 z*we#pk$sLLl7AH7|72EJ2MBlJD5QQ){F;eZEoY~sGSE~*>wD2#Vu5>;59y6Bbkp}% z+W~pPd0DcuKg>-^_py_-cdl;KtH<6kgWlqR&-Hd&vCyz*>LK_o-@f~ny?%?L5d^48 z>N)KUYde0dRGZgwIrS+b_-4~l0(D$^P|ZNL6gd0$9ZHg%`eFcyL>>$+clq9BUsPr^ zvyo(fDrpi1twt9k?j&UPbjLYex^|fc@MEy7#($hVT5>m9d1@t>+8Qb-9VjfFQS}^6 z<}5tw=iIjev>W6yi96Ryn%7R%uQs}ZW2U_W{6Z=%@t0NAnS$BVf>}&P)g>PS5U^0G zE|u10f3IvbXL&Zhec%(b)G}Vh0G}*~J)rqQk%^oz zGSGCyHqXc^gRp zxZ{{`bDDP6ZT)shlVQP3ulU4!AS6@0ho44nqg`h~VzK5rOfCO9%^V~bIyT*K&Vjidp~Xz;z~7gFDBR6*fWwJ zt71UA9cLt_=3LdiY0^6f;z7wtKCv0IBy5zT78B}!?`J!m;hI5-Q4ANsNR^&=7NHm~ zBW(xSQ*K8@&JzC`J1W(Q!buT!eb{K4U2n6N0Vdh>%FeqOOw@72dAGNc-kAy|-Bi<5 zTkqk~>DJbp=8+RiN_Z{Z&I zm&c=A@!h?%HH5<`tLDRu&HFeV)9knSW-u+!Ym|f4ycs8rC*2h&VVPs?jPc&|mCMP6 zOHN7f0B)VU+0C&smh0dKUy{0P!++^Os~P`l-4s6@klr2BCl_BIzKZ4lRChk&cYTp2 zegB~b#mRzpsgaisK!rbFEttKW!>Fsi&;~x;Jld%jsML(d%uh8Rhfj|9vKf zCmXj~S@9x(?>=NQc1KJl)cn?Alw&zHJEY#Mf$6%}-uZ0C8Py15Er!_#J6FuAf{;2wGXt_ zp~692g0<4*t5-bluNFik-%a)2q0^}eub5nqg4mvSS=&8jsRtGegw-~;0QT5+e_}T) z8v^;gu2H(8Th+dg*Ig>sFuh2h?m3Ir`in%t!+UNyx&9N|XCT|uaT;C6CjmqY(x?XS zuUueyz=yEGzV>dpKT2-<%d?lK;Zak7tb_qRI}p62VB1qwy~($Q!L%_Gfr;a>Lu)5L`*r<% zqMqIw7tjY!XcdKti`;MiTu6~H?k-1gJLIwP3Ij1{WMPMW4!n;H&sTXj75k06iT=j{ zr=9lQ=!3Eo0oQS5GGSb{kVno{NsboB+kIosS%<5BMq0!e#-SCm!B~Ms=g>{ymPI+l5Wt-0NTq1D;4XBHl+YXO!I9disW*Sl zmLD-S5@SKut849{{BV124HiNh=YD~l%Vy;y+%=f9wzP5y5%&|A_M&(?p>)%4F4fMk zqDCki)Ks(UK-etD)o6u|zQ#kS$RfYOJ>+9xSmrBNCgM*n_zg%#)2A^!3IT}oY&x;q z;{iQ|0i}Uh1|xN+jy>|ey|fu|e_TnzRI~DD2$e_I=M@vWp-_d`fz4!PiBo$|?nA=bdiK?N*&&eTaF+EgG+lSZ zp`=6lPoy~~a<5u18#s>-(8`xc}_ z6mTJmbf*&1sVLpGXz2z)x&%a|ySux)K}uS>yQI5gy$4?RzMp%Kz3=CJu6OJ&?-5Hni_RUHz{6 zH^7<`NX?Y#igGwrnRLqF;IDLoq$1(C8P3sHV zt{jx7R(UU00PYE;#2wv*eX<$RpdI({EivdzJjKtBm(w-lQqn)Jl7#H}OA+WjAdHjf z5PQa%(jM@|eb+Z8(so47{i=5%NGxOxO+U&8rXJbY<6X%hv-N_^Gk~{z&6+Y9p~KsW8q~a@uwoPo~P%cYX-AX-N|

    v~Q6&(;ZwL10uZEA?qSYN%98amu4r*(J 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�C62a$e$nY;Ex%!Obt8ep9Zm> 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⪙dn<#E9p-=uewVKbbV@fQp#JVw%DcR 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

    &ao&jnNg<2aZsi@1KL8QO>1f61 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
    |u0o1R!*Jt6k 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 literal 142848 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 WtD3OqQVRn7lMwwXQZA(D|Gxl=ygnuX diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc index 66edbc95526e..e854904b6baf 100644 --- a/docs/osquery/osquery.asciidoc +++ b/docs/osquery/osquery.asciidoc @@ -61,12 +61,11 @@ TIP: To save a single query for future use, click *Save for later* and define th [[osquery-view-history]] == View or rerun previous live queries -The *Live queries history* section on the *Live queries* tab shows a log of queries run over the last 30 days. -Each query has the following options: +The *Live queries history* section on the *Live queries* tab shows a log of queries run over the last 30 days. From the Live queries table, you can: -* Click image:images/play-icon.png[Right-pointing triangle] to rerun a query. +* Click the run icon (image:images/play-icon.png[Right-pointing triangle]) to rerun a single query or a query pack. -* Click image:images/table-icon.png[Table icon] to view the query <> and <>. +* Click the table icon (image:images/table-icon.png[Table icon]) to examine the <> for a single query or a query pack. From the results table, you can also find the query <>. + [role="screenshot"] image::images/live-query-check-results.png[Results of OSquery] From e17ce2ef312b00c1671ea1bb59535fedbe29dd70 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 30 Sep 2022 13:19:42 -0600 Subject: [PATCH 174/185] skip failing test suite (#141782) --- .../spaces_only/apis/bulk_create.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts index 4e5059933dd7..34e18b76088f 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts @@ -116,7 +116,8 @@ export default function ({ getService }: FtrProviderContext) { }); }; - describe('_bulk_create', () => { + // Failing: See https://github.com/elastic/kibana/issues/141782 + describe.skip('_bulk_create', () => { getTestScenarios([false, true]).spaces.forEach(({ spaceId, modifier: overwrite }) => { const suffix = overwrite ? ' with overwrite enabled' : ''; const tests = createTests(overwrite!, spaceId); From ed8cf20f3a075e8ef460f65a060f2b26420dcef4 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 30 Sep 2022 15:02:41 -0600 Subject: [PATCH 175/185] skip failing test suite (#142118) --- x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts index 7d6cc5ea9881..a3314aabce72 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts @@ -16,6 +16,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); // FLAKY: https://github.com/elastic/kibana/issues/142118 + // Failing: See https://github.com/elastic/kibana/issues/142118 describe.skip('jobs cloning supported by UI form', function () { const testDataList: Array<{ suiteTitle: string; From 1b09c8268e4df1bd3f5ade13ade672eceb9ceb74 Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Fri, 30 Sep 2022 16:10:09 -0500 Subject: [PATCH 176/185] [TIP] Fix barchart legend rendering issue (#142268) * [TIP] Fix barchart legend rendering issue - set minimum width - vertically align context menu button - remove value displayed in legend --- .../indicators/components/barchart/barchart/barchart.tsx | 5 +++-- .../components/barchart/legend_action/legend_action.tsx | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx index 2f4fecbd930a..f15a1f2e4cd3 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/barchart/barchart/barchart.tsx @@ -16,6 +16,7 @@ import type { ChartSeries } from '../../../services'; const ID = 'tiIndicator'; const DEFAULT_CHART_HEIGHT = '200px'; const DEFAULT_CHART_WIDTH = '100%'; +const DEFAULT_LEGEND_SIZE = 200; export interface IndicatorsBarChartProps { /** @@ -27,7 +28,7 @@ export interface IndicatorsBarChartProps { */ dateRange: TimeRangeBounds; /** - * Indicator field selected in the IndicatorFieldSelector component, passed to the {@link AddToTimeline} to populate the timeline. + * Indicator field selected in the IndicatorFieldSelector component, passed to AddToTimeline to populate the timeline. */ field: string; /** @@ -50,8 +51,8 @@ export const IndicatorsBarChart: VFC = ({ } /> setPopover(!isPopoverOpen)} + onClick={() => setPopover((prevIsPopoverOpen) => !prevIsPopoverOpen)} + style={{ height: '100%' }} /> } From a0d395d4a733bb640789175b347399384b966fc2 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 30 Sep 2022 22:39:27 -0600 Subject: [PATCH 177/185] [api-docs] Daily api_docs build (#142422) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.devdocs.json | 255 ++++++++++++++++++ api_docs/data_views.mdx | 4 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- .../kbn_core_injected_metadata_browser.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.devdocs.json | 157 ++++++++++- api_docs/lens.mdx | 4 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 12 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.devdocs.json | 15 ++ api_docs/ui_actions.mdx | 4 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.devdocs.json | 56 +++- api_docs/unified_search.mdx | 4 +- api_docs/unified_search_autocomplete.mdx | 4 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 408 files changed, 894 insertions(+), 417 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 27394a9d1337..48d75bc3ce7c 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: 2022-09-30 +date: 2022-10-01 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 50e964f1d4fc..ee9b4276cce6 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 97c9deaf7e16..9d860aa24355 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: 2022-09-30 +date: 2022-10-01 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 1d4419372cb8..e72baca86cbf 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: 2022-09-30 +date: 2022-10-01 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 894f1a412400..020843029e21 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index a88ae007a49f..b792f664c347 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: 2022-09-30 +date: 2022-10-01 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 9aa4dc3dc6cb..a343745fd134 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: 2022-09-30 +date: 2022-10-01 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 98d04fe776c1..74326b5f4e70 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: 2022-09-30 +date: 2022-10-01 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 8e0ec02de952..6d8fcf94ddb8 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: 2022-09-30 +date: 2022-10-01 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 0daa29277e4c..f2eb1e6d668c 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: 2022-09-30 +date: 2022-10-01 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 8794f842b346..a7f1c3bd483c 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 67f93229e131..4cd830231b26 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index e1a21c6f2cc8..7afab612c4a7 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: 2022-09-30 +date: 2022-10-01 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 8ba5fe4e3728..1ace57ec36bd 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index b0885214467d..1e2f85a3b6c6 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 38ceacd3fae0..e3d2f1354aea 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 99cc96819059..62deee07bd7d 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: 2022-09-30 +date: 2022-10-01 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 f429f1771909..abecd96470ec 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: 2022-09-30 +date: 2022-10-01 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 1b406c8cf22e..6a49051f2936 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: 2022-09-30 +date: 2022-10-01 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 6b45be67575c..44fc64c6bdf4 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 9c7512fae826..a82d817ae131 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: 2022-09-30 +date: 2022-10-01 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 6131baf37850..3e7214a59cc2 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index eb804887cbbb..288b3ee585f5 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: 2022-09-30 +date: 2022-10-01 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 0360e887a0f1..f0c7b8022d14 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: 2022-09-30 +date: 2022-10-01 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 cf2021543491..9840337f75a7 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index c86863415866..cc6bd0a259d0 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -4164,6 +4164,45 @@ "returnComment": [], "children": [] }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublic.getIndices", + "type": "Function", + "tags": [], + "label": "getIndices", + "description": [], + "signature": [ + "(props: { pattern: string; showAllIndices?: boolean | undefined; isRollupIndex: (indexName: string) => boolean; }) => Promise<", + { + "pluginId": "dataViews", + "scope": "public", + "docId": "kibDataViewsPluginApi", + "section": "def-public.MatchedItem", + "text": "MatchedItem" + }, + "[]>" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublic.getIndices.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "{ pattern: string; showAllIndices?: boolean | undefined; isRollupIndex: (indexName: string) => boolean; }" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "dataViews", "id": "def-public.DataViewsServicePublic.hasData", @@ -5398,6 +5437,101 @@ "path": "src/plugins/data_views/public/data_views_service_public.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices", + "type": "Function", + "tags": [], + "label": "getIndices", + "description": [], + "signature": [ + "(props: { pattern: string; showAllIndices?: boolean | undefined; isRollupIndex: (indexName: string) => boolean; }) => Promise<", + { + "pluginId": "dataViews", + "scope": "public", + "docId": "kibDataViewsPluginApi", + "section": "def-public.MatchedItem", + "text": "MatchedItem" + }, + "[]>" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices.$1.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices.$1.showAllIndices", + "type": "CompoundType", + "tags": [], + "label": "showAllIndices", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices.$1.isRollupIndex", + "type": "Function", + "tags": [], + "label": "isRollupIndex", + "description": [], + "signature": [ + "(indexName: string) => boolean" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsServicePublicDeps.getIndices.$1.isRollupIndex.$1", + "type": "string", + "tags": [], + "label": "indexName", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/public/data_views_service_public.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ] + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -5906,6 +6040,68 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "dataViews", + "id": "def-public.MatchedItem", + "type": "Interface", + "tags": [], + "label": "MatchedItem", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.MatchedItem.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.MatchedItem.tags", + "type": "Array", + "tags": [], + "label": "tags", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "public", + "docId": "kibDataViewsPluginApi", + "section": "def-public.Tag", + "text": "Tag" + }, + "[]" + ], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.MatchedItem.item", + "type": "Object", + "tags": [], + "label": "item", + "description": [], + "signature": [ + "{ name: string; backing_indices?: string[] | undefined; timestamp_field?: string | undefined; indices?: string[] | undefined; aliases?: string[] | undefined; attributes?: ", + "ResolveIndexResponseItemIndexAttrs", + "[] | undefined; data_stream?: string | undefined; }" + ], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "dataViews", "id": "def-public.RuntimeField", @@ -6339,6 +6535,53 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.Tag", + "type": "Interface", + "tags": [], + "label": "Tag", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.Tag.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.Tag.key", + "type": "string", + "tags": [], + "label": "key", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.Tag.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "enums": [ @@ -6355,6 +6598,18 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.INDEX_PATTERN_TYPE", + "type": "Enum", + "tags": [], + "label": "INDEX_PATTERN_TYPE", + "description": [], + "path": "src/plugins/data_views/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "misc": [ diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 9e9e13d1c714..9ce3b6c70063 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 966 | 0 | 208 | 1 | +| 983 | 0 | 225 | 2 | ## Client diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index a31d748cb362..05d260b1c544 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 32c98155e1b6..a3d96055c821 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 52ccce27de73..4eb2eb95d80e 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 28ee76e3644f..4fc141f38747 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 9753d6519463..f22d72807964 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: 2022-09-30 +date: 2022-10-01 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 d99574811ed8..c744310a34b9 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: 2022-09-30 +date: 2022-10-01 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 46669e83965b..b9b4af0426fd 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 19cbeac71650..f09224c58150 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: 2022-09-30 +date: 2022-10-01 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 cad3566677f9..e1530fd32233 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: 2022-09-30 +date: 2022-10-01 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 0964ad2d3db4..3f0a323b9279 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: 2022-09-30 +date: 2022-10-01 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 25628d88904b..68f9252a936a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 31bbfd3cfa77..e4b29fe30fce 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 70931ba483ca..5fc5dd479579 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 57cb17ed7f57..a1aaa7507ccb 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 87f06ab7f0fc..a7413684bd03 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: 2022-09-30 +date: 2022-10-01 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 e2d8790bf2b7..a882614c990a 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: 2022-09-30 +date: 2022-10-01 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 03060b8617a5..9237719c9d20 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: 2022-09-30 +date: 2022-10-01 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 7ccb3fec0ef4..aae35271ff39 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: 2022-09-30 +date: 2022-10-01 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 ea8581c29a05..df673151690c 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: 2022-09-30 +date: 2022-10-01 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 b7d3b7b43451..da11f3077c1a 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: 2022-09-30 +date: 2022-10-01 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 901d559b5a7e..846aa497ea3b 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: 2022-09-30 +date: 2022-10-01 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 4f53bb28008e..02618661f080 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: 2022-09-30 +date: 2022-10-01 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 aeff45f40a77..02f01b4dd86f 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: 2022-09-30 +date: 2022-10-01 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 096441865046..acd06327d888 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: 2022-09-30 +date: 2022-10-01 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 237430ae0fb8..8046b493fef2 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: 2022-09-30 +date: 2022-10-01 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 2316d7e48cc9..3ddfd870314a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 6c70b3fd09b1..0b5d88e7aab7 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index a2c88ec02316..8c0901015111 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: 2022-09-30 +date: 2022-10-01 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 821587d02923..05df2ffc3a48 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: 2022-09-30 +date: 2022-10-01 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 77ec5bd6a6db..fb46fbf3c533 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 71fd5ede2e0a..ad24537aa0fd 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: 2022-09-30 +date: 2022-10-01 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 5536609e6bf4..77286018c623 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 61843d4ecb35..8196936c8efd 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 8681c30dcf97..6908d7194d17 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: 2022-09-30 +date: 2022-10-01 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 01249c9253db..af5a128f54b0 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: 2022-09-30 +date: 2022-10-01 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 21a00dd18b77..8bbe725e782f 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 08133b2e0ce4..9edd7ca01eff 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: 2022-09-30 +date: 2022-10-01 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 e6a58683dd57..0c3c64c38fa9 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 27d270ef0349..12e3a5944bfe 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 69d8e811d233..d5d9c2b4b693 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index d425e08214e0..3f156bfcb7b3 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 85eedf5bc8cb..9033fdf982ef 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 0fcb70c426aa..dae5a070183a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 7a5a7dc343e1..25f9c2c92bfd 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index d3da6e6fc627..ca9ca177b00a 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index b19adbf23a6b..9c5c4346e45a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index cb3267bf94c0..0f94bac6c9c8 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 6f92758ba29f..71847c01b1eb 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 42ba3bd8ac0d..09e9cdc683fa 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 9e24183eab48..14753eade649 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 642cfc316df0..7b98319dfa0d 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 64f41b4de638..7e89a7719619 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: 2022-09-30 +date: 2022-10-01 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index a07ae6ec5b38..a8302426d0f9 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index ccabe37aa9fb..8e3217250a2f 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 904c2db7eeb9..b96fac650f3b 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 3ef51f85a1dd..2e27eedb97be 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: 2022-09-30 +date: 2022-10-01 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 4dbba9e0e4bb..ba566208fe8b 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: 2022-09-30 +date: 2022-10-01 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 a3510af5f70b..71b3793b5edb 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: 2022-09-30 +date: 2022-10-01 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 c1bdc2321405..b2e6e11bf6f5 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: 2022-09-30 +date: 2022-10-01 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 dd6763ce0a98..741e1c8d09dc 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index b8a547e074cd..534685b36cd2 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: 2022-09-30 +date: 2022-10-01 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 f2993b49d5f8..3ff3d642b506 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: 2022-09-30 +date: 2022-10-01 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 62491682503d..67fdb94f78f3 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: 2022-09-30 +date: 2022-10-01 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 cf14ce5a40d5..3b43b92a5565 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 11c9a0928b58..84ad21fc47fd 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 76cb685c99f3..8c307425ee04 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: 2022-09-30 +date: 2022-10-01 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 6c4d86bf0b72..3212fb673e27 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: 2022-09-30 +date: 2022-10-01 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 34614865e7e8..4e9dd3d54d35 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: 2022-09-30 +date: 2022-10-01 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 1342382025c6..7bd027365d6e 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: 2022-09-30 +date: 2022-10-01 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 da2faf312d36..76cd9dba2db5 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: 2022-09-30 +date: 2022-10-01 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 f2810dc16d62..bfa75dd3afd7 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: 2022-09-30 +date: 2022-10-01 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 b002114d3e33..7f9da63f298a 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: 2022-09-30 +date: 2022-10-01 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 c726e5f8b787..2f2615a48955 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: 2022-09-30 +date: 2022-10-01 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 fe234ab156ec..9c806330e366 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: 2022-09-30 +date: 2022-10-01 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 f0450f42dbb9..9294b5b708f8 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: 2022-09-30 +date: 2022-10-01 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 b486574b88c2..af582f9ff198 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: 2022-09-30 +date: 2022-10-01 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 6c09e87c4cc0..8af8f902e540 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: 2022-09-30 +date: 2022-10-01 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 6f23a43cb533..aaf8373e9ca7 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: 2022-09-30 +date: 2022-10-01 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 98cc757ce42d..9ed7dbaa5e2d 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: 2022-09-30 +date: 2022-10-01 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 69b473e30ee8..8186b42e0220 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: 2022-09-30 +date: 2022-10-01 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 be0a688924da..a0c063e84009 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: 2022-09-30 +date: 2022-10-01 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 8eee81e91198..221ae3385ddd 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: 2022-09-30 +date: 2022-10-01 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 48ebe7f76b16..2156646c76f0 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: 2022-09-30 +date: 2022-10-01 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 6c6318b81245..f6c8668e76f2 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: 2022-09-30 +date: 2022-10-01 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 0087fd5a9837..3d39b753f2ab 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index cb7a24e95c61..6e6ffeac0f65 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: 2022-09-30 +date: 2022-10-01 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 b98bb79392f8..203f9a365fde 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: 2022-09-30 +date: 2022-10-01 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 83a912dd03fd..cd573bfd8776 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: 2022-09-30 +date: 2022-10-01 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index a3f274459a41..e2134b925ba8 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: 2022-09-30 +date: 2022-10-01 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 eeb3c991a0d6..9fc96d5d598f 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: 2022-09-30 +date: 2022-10-01 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 2eb4bda78a5c..7a6634ac7f8d 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index 1edd0ca68c53..edcf87b96094 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 35470535f96e..35e8a6ce2632 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: 2022-09-30 +date: 2022-10-01 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 b9f9b064de30..8167097daa0d 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: 2022-09-30 +date: 2022-10-01 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 118225780a1f..b45ecd8257b0 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: 2022-09-30 +date: 2022-10-01 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 c1331c1caec0..031456455b1e 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: 2022-09-30 +date: 2022-10-01 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 7c77af97bad8..381310fcf154 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: 2022-09-30 +date: 2022-10-01 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 3e100243e211..05f768b81b3c 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: 2022-09-30 +date: 2022-10-01 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 83c04c073ea9..52776680286a 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: 2022-09-30 +date: 2022-10-01 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 ea9ad18fb0ac..63cc80d8fa73 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: 2022-09-30 +date: 2022-10-01 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 524e9382c019..309f4ef8839d 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: 2022-09-30 +date: 2022-10-01 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 2c5d8ce0deee..d4df75707a94 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: 2022-09-30 +date: 2022-10-01 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 dd5df0e31e51..a5ade116a7c0 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: 2022-09-30 +date: 2022-10-01 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 3e6df91ab7eb..9a09fe50975c 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: 2022-09-30 +date: 2022-10-01 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 679fdb16b2af..7fffb0ccbd3d 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: 2022-09-30 +date: 2022-10-01 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 bdb596d8b29e..7c8733b369f0 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: 2022-09-30 +date: 2022-10-01 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 2832e5b25baa..f283e05e563d 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: 2022-09-30 +date: 2022-10-01 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 765f996d9c46..d40d5501ca99 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: 2022-09-30 +date: 2022-10-01 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 dd30432a2a83..cc1a420ccba1 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: 2022-09-30 +date: 2022-10-01 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 f91bd759633c..25f0346340ea 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: 2022-09-30 +date: 2022-10-01 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 06e0a3a543a0..87eb2c5dd2fd 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: 2022-09-30 +date: 2022-10-01 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 031735030635..1023c4c175a3 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: 2022-09-30 +date: 2022-10-01 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 5a354609a0a3..20aa61f141c8 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: 2022-09-30 +date: 2022-10-01 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 d520c20c2d3b..4fdaf9d97f6f 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: 2022-09-30 +date: 2022-10-01 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 bc6c34d1c07f..81e62c8036e0 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: 2022-09-30 +date: 2022-10-01 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 3faf57a4c881..548174fbd5d8 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: 2022-09-30 +date: 2022-10-01 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 83a7e913c082..16c6d6b51020 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: 2022-09-30 +date: 2022-10-01 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 10055c2130fd..094479784c51 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: 2022-09-30 +date: 2022-10-01 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 acb60f56ebfc..41f882912cfe 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: 2022-09-30 +date: 2022-10-01 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 9130f1f857a9..b00edeb7d68b 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: 2022-09-30 +date: 2022-10-01 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 4d1bfc14b6a6..17d48963b8b7 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: 2022-09-30 +date: 2022-10-01 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index d548463315b2..2ee35b66a8f6 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index b873c4a15d6e..750d6c7b3bef 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_http_server.mdx index 7e6e71fa6195..3a9e92dbe650 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 8186ddd0f39a..27352519951f 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index fdeeded54cd6..2aa52e0dc1c0 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: 2022-09-30 +date: 2022-10-01 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 cc8e5edcd399..a8e82e67d22c 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: 2022-09-30 +date: 2022-10-01 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 2ca59b2bad48..10be37f40c29 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: 2022-09-30 +date: 2022-10-01 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 ba5a6502efe1..ef2963239008 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: 2022-09-30 +date: 2022-10-01 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 8847538b3a2d..d6ed176629a2 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: 2022-09-30 +date: 2022-10-01 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 e992ae37a3b7..ee1ed897ede0 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 39b237eedd45..7b120559dc17 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 9e750bb7dba9..3e7af178ec12 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: 2022-09-30 +date: 2022-10-01 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 ed82f8a5dea4..d2aabf046c88 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: 2022-09-30 +date: 2022-10-01 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 f9f9e8a948c6..bc12a2785cc0 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: 2022-09-30 +date: 2022-10-01 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 f9376e67d699..7480b362f29b 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: 2022-09-30 +date: 2022-10-01 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 fa9f4053803d..f21296396e1d 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: 2022-09-30 +date: 2022-10-01 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 7f27d332faca..61be5ea720a4 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: 2022-09-30 +date: 2022-10-01 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 3d5bfb44851b..af6dd35e8312 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: 2022-09-30 +date: 2022-10-01 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 84ab6fff88ff..a09ca7932e57 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: 2022-09-30 +date: 2022-10-01 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 3e9e378809f2..c01ad905bc16 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: 2022-09-30 +date: 2022-10-01 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 6f502bf2c3bc..d31c4dd48ee5 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: 2022-09-30 +date: 2022-10-01 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 85f6d9268f7c..9f15f6cf7390 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: 2022-09-30 +date: 2022-10-01 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 fcc2c9452311..e93ec0476f25 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: 2022-09-30 +date: 2022-10-01 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 5683a2d792ef..5a94cd10fcb0 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: 2022-09-30 +date: 2022-10-01 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 8b1732b474d9..43e64914cfb3 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: 2022-09-30 +date: 2022-10-01 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 cb8ab729b92d..557e650a73b5 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: 2022-09-30 +date: 2022-10-01 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 983e300999f7..a3ebabf7f32c 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: 2022-09-30 +date: 2022-10-01 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 6832c1707041..804d680bae80 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: 2022-09-30 +date: 2022-10-01 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 b86953b92d74..e15645fb9ab8 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: 2022-09-30 +date: 2022-10-01 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 d400da3e6594..a64c369bcba4 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: 2022-09-30 +date: 2022-10-01 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 457f058a1c21..1192b6244a3a 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: 2022-09-30 +date: 2022-10-01 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 03fa3b8be079..0eb8bc161976 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: 2022-09-30 +date: 2022-10-01 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 187d1c690e2d..d8b0afcc889b 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: 2022-09-30 +date: 2022-10-01 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 6dca74dc0384..7982df2fb243 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: 2022-09-30 +date: 2022-10-01 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 8ef53aa00edb..9c70f02f78d0 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: 2022-09-30 +date: 2022-10-01 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 f023cecb27a9..6bd600cfb198 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: 2022-09-30 +date: 2022-10-01 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 89514146e62f..be27903d8b48 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: 2022-09-30 +date: 2022-10-01 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 5f530e17cbf2..b4745db58332 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: 2022-09-30 +date: 2022-10-01 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 3f6c17f74aba..47752b2ae672 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: 2022-09-30 +date: 2022-10-01 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 4d731485bd3b..856ab9b0e54b 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: 2022-09-30 +date: 2022-10-01 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 6297a248ab16..79709ae89848 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: 2022-09-30 +date: 2022-10-01 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 7784682515d4..b88c15571412 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 9b590bd554e8..f28c4920181c 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: 2022-09-30 +date: 2022-10-01 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 321c7d8761b6..2bf85267549a 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: 2022-09-30 +date: 2022-10-01 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 13287c8f8e30..3f364ace01fd 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: 2022-09-30 +date: 2022-10-01 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 bb9f4b645d23..2b9b805aa0d5 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: 2022-09-30 +date: 2022-10-01 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 676a2bb19365..94ee2b5e95dd 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: 2022-09-30 +date: 2022-10-01 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 93c77175614e..f2e58e7919b5 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: 2022-09-30 +date: 2022-10-01 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 3d8036576123..2f9686598dae 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: 2022-09-30 +date: 2022-10-01 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 d9f37e38d76b..67a62a9d8f82 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: 2022-09-30 +date: 2022-10-01 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 77f3b314f46f..f006d43902df 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: 2022-09-30 +date: 2022-10-01 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 1a389643784b..962c7d5ac011 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: 2022-09-30 +date: 2022-10-01 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 5f12f65364d0..c07112e6ad54 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: 2022-09-30 +date: 2022-10-01 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 945817f8b5ed..40a150c47507 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: 2022-09-30 +date: 2022-10-01 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 128f95a96f7f..9d63c473addd 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: 2022-09-30 +date: 2022-10-01 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 52533c9b6fe3..27238b71e947 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: 2022-09-30 +date: 2022-10-01 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 e1795a3be133..6ca747830ab4 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: 2022-09-30 +date: 2022-10-01 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index d6ff451e1d1d..768e628a8af9 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: 2022-09-30 +date: 2022-10-01 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 e3a2233f32a1..970cb7ab087e 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: 2022-09-30 +date: 2022-10-01 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 2a073386f7a7..cd7bbe51e904 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: 2022-09-30 +date: 2022-10-01 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 da2f56a7f69c..da072c5533de 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: 2022-09-30 +date: 2022-10-01 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 db4c4003ec79..0a9599ba8cac 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: 2022-09-30 +date: 2022-10-01 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 c8b0a759e9da..57940d8a2498 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: 2022-09-30 +date: 2022-10-01 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 a46dd1b958ad..ba62b2164435 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: 2022-09-30 +date: 2022-10-01 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index bf4074f9ff80..c3939a291a65 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: 2022-09-30 +date: 2022-10-01 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 50d9d7c37816..6148fce41049 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 876f4bd186bb..5fb0272483d5 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: 2022-09-30 +date: 2022-10-01 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 433f7324e549..28de861c3382 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: 2022-09-30 +date: 2022-10-01 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 0bf1ae7c2410..d9a6c2b14d25 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: 2022-09-30 +date: 2022-10-01 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 577822a31bc1..3dd7677225f0 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: 2022-09-30 +date: 2022-10-01 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 30759d2ba604..8a73e4407035 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: 2022-09-30 +date: 2022-10-01 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 b00037c8b73c..b86978a4c0f5 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: 2022-09-30 +date: 2022-10-01 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 0dbf6764d598..632bc077b3e1 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: 2022-09-30 +date: 2022-10-01 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 f2e11e0ec410..43ce870fd9d1 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index fdafd7c74c00..982847f0d737 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 8e7cee968fe7..55d78bd6eee1 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: 2022-09-30 +date: 2022-10-01 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index bfcb2471b8f0..4a0c5588f0be 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: 2022-09-30 +date: 2022-10-01 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_crypto.mdx b/api_docs/kbn_crypto.mdx index d47e0499bc2a..521e5b631e39 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: 2022-09-30 +date: 2022-10-01 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 471c3b25429d..58ddc73e08df 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 4dc98f350e51..644ca142bc7d 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 0691a727d305..61773de18ba4 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: 2022-09-30 +date: 2022-10-01 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 7f92e41d7a4d..33f29af6449f 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: 2022-09-30 +date: 2022-10-01 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 720562339cb0..7160587ae7c0 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: 2022-09-30 +date: 2022-10-01 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 9b0d11e055ba..765e362ac500 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index a972f8dfe08c..d74e0af0ba44 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: 2022-09-30 +date: 2022-10-01 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 7fde88f253ff..bd64af2bd9d2 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index b60e5a143225..16b1791d70aa 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 5b8334a6273f..516930bec71f 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: 2022-09-30 +date: 2022-10-01 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 cbd8011fd264..f8cfd1377e56 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: 2022-09-30 +date: 2022-10-01 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 6454ec64eeec..0125789a530a 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: 2022-09-30 +date: 2022-10-01 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 9d9912a3e60b..faa1089fab65 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: 2022-09-30 +date: 2022-10-01 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 460d6cc14eab..756b802d0c32 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index de71112dafad..bb82f2d25977 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 4e028126633c..f745989c9c8a 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: 2022-09-30 +date: 2022-10-01 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 04d7cb7b4cfd..6ddc526b2548 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: 2022-09-30 +date: 2022-10-01 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_generate.mdx b/api_docs/kbn_generate.mdx index d88b4e834bee..42e063e32e7f 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 11f578d5d6c5..984eab3d218e 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index b3a8defe7720..0fcc343e3d19 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: 2022-09-30 +date: 2022-10-01 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 e9429b41deb7..2b73efa6062a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index f678cb181de1..0dc1d0876d3a 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: 2022-09-30 +date: 2022-10-01 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 404f2163c46c..f8639118d81e 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: 2022-09-30 +date: 2022-10-01 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 525cb60f347a..78d78be33902 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index d4f49a570463..f8c83ac7998a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 9fa8dfc8321c..894230ad1d11 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 4c08eeab4c88..034bcc168b9e 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index d8948bee8640..417f8ba3a65f 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: 2022-09-30 +date: 2022-10-01 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 e2a2cc005cad..7e46d2dcb1f9 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 3999084bdf28..5dc82ade1a64 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 6f279b0700ac..e88bca58e89e 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: 2022-09-30 +date: 2022-10-01 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 cc05defa4dac..dee1f848efd6 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 20c5f25e026b..ef7abd6df1bf 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index a0dfd1545060..cb067e4a5e48 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 8c10def45c78..2c28a423148f 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: 2022-09-30 +date: 2022-10-01 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index f4eb153961bd..e1380d56c305 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: 2022-09-30 +date: 2022-10-01 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 8d36ae013263..960bb4da53ce 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 2a32d7d919b0..00938629a961 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index ad7149dc8fe1..0a5ae8a689d8 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: 2022-09-30 +date: 2022-10-01 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 8d83ba6d1f0e..b6440aacaa07 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: 2022-09-30 +date: 2022-10-01 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 9bd69c34940d..f92a80367e02 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: 2022-09-30 +date: 2022-10-01 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index e87e2bea0731..56e2cd559c03 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: 2022-09-30 +date: 2022-10-01 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index dff42547b8b4..23bb7dc57c4e 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: 2022-09-30 +date: 2022-10-01 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 9dfa323c764a..c685665c1cd4 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 233fdfa2b529..e603b0c70740 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index e7e506f42c4d..0aa2a7e6d7e2 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 2385663d0372..d327087b2929 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index c9560b881efc..3912476bf911 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 277e8fe2e29f..2f31f51131f6 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: 2022-09-30 +date: 2022-10-01 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 aada63b6cdcf..4777f33dc85d 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: 2022-09-30 +date: 2022-10-01 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 06a05479ba72..07b9c7684cbb 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: 2022-09-30 +date: 2022-10-01 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 4ff62a3c86d9..3c721f46f48c 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: 2022-09-30 +date: 2022-10-01 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 73431dba715a..1d452af9ea15 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: 2022-09-30 +date: 2022-10-01 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 f1065703e694..59df749d4429 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: 2022-09-30 +date: 2022-10-01 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 0a3e38566ee9..8063f1fd1592 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: 2022-09-30 +date: 2022-10-01 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 0dc5e87dba56..184877c74cfa 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: 2022-09-30 +date: 2022-10-01 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 ee9a5057cd0b..933a87c620ba 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: 2022-09-30 +date: 2022-10-01 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 75f29aaf7123..0f3f3805ca32 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: 2022-09-30 +date: 2022-10-01 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 515b4a8285bb..6b810acdc8e7 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: 2022-09-30 +date: 2022-10-01 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 2991cdacbacd..aceb1ec1d65d 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: 2022-09-30 +date: 2022-10-01 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 92b1867f34a6..11cfe00c5eb6 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: 2022-09-30 +date: 2022-10-01 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 a9ff8d1d81fb..4e4ba39e56d9 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: 2022-09-30 +date: 2022-10-01 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 91cf5bb3b5dd..5a002fe9b634 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: 2022-09-30 +date: 2022-10-01 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 cefe3d1543a9..53bffb726d7e 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 737c986cbf11..ba975b844692 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: 2022-09-30 +date: 2022-10-01 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index eebd51935c35..9c30c4297261 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index c121d1e8a489..cf6491d338fe 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index ff5390338683..e055ce5c7dbe 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: 2022-09-30 +date: 2022-10-01 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 12b820666a65..63958a2cb029 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: 2022-09-30 +date: 2022-10-01 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 c083f9ea1266..e8dacbe7203b 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: 2022-09-30 +date: 2022-10-01 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 35439d9fda00..421f7f9d7187 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: 2022-09-30 +date: 2022-10-01 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index de78cc05fd16..99d63502639c 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: 2022-09-30 +date: 2022-10-01 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 b1c6f91ebdbd..2deff8486497 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: 2022-09-30 +date: 2022-10-01 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 9f07e742a760..883bd3ab03a8 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: 2022-09-30 +date: 2022-10-01 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 065a90d00a34..0db5daddc45c 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: 2022-09-30 +date: 2022-10-01 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 c100281c25d4..a9353ec77404 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: 2022-09-30 +date: 2022-10-01 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 b884d72ca4d0..99d9c415c15b 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: 2022-09-30 +date: 2022-10-01 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 c060047529bc..2417b72b9ffe 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: 2022-09-30 +date: 2022-10-01 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 362eb12bc58f..68dfc645ddd8 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: 2022-09-30 +date: 2022-10-01 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 4e02c245ff69..8f977f1cc7a9 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: 2022-09-30 +date: 2022-10-01 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 49e658a38c8b..109a924fb3b9 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: 2022-09-30 +date: 2022-10-01 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 5e2e62553998..07a01ac671da 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: 2022-09-30 +date: 2022-10-01 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 eec2b418bbc4..10af2f7f2c16 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: 2022-09-30 +date: 2022-10-01 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 f6bf840ae41a..0b0f2e48ec0a 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: 2022-09-30 +date: 2022-10-01 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index deb460019bbe..956de3dd0584 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: 2022-09-30 +date: 2022-10-01 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 b6141feed65d..f10769e16c69 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: 2022-09-30 +date: 2022-10-01 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 9d6b7561cd4f..a99179ef9d40 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: 2022-09-30 +date: 2022-10-01 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 1d57383c1f7a..6abf6549e056 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: 2022-09-30 +date: 2022-10-01 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index f1529dbdeead..e44419942c79 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 08e81f53f5e6..e25a62c5079a 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: 2022-09-30 +date: 2022-10-01 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index f9a40e4e5060..28fe6feb5d19 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 0be3ac9e885d..05b56017a08f 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: 2022-09-30 +date: 2022-10-01 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 12855b1d6c6e..be9d33d159f3 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: 2022-09-30 +date: 2022-10-01 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 ff504977fbd1..dd904c049f3b 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index c4980baa0846..b854992fde33 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: 2022-09-30 +date: 2022-10-01 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 fc2d3aa4ceb2..82eab77aca0c 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index c0fe27305880..bd59838a870d 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: 2022-09-30 +date: 2022-10-01 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 a11f331d327f..c5b72af903b3 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 634503f23763..abd0555459d9 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index ec79f6d74e50..09bc46b76cc9 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 64a86ae7f718..60509b4421a9 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index fd83134954ef..614b52b535a9 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: 2022-09-30 +date: 2022-10-01 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index e05e7d1fbf27..86b99ea131b4 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index c62021b90a7c..1411d1f11e0f 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: 2022-09-30 +date: 2022-10-01 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 383e50529e74..124801b6982e 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: 2022-09-30 +date: 2022-10-01 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 900ea8d6e615..286cf41a0f81 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: 2022-09-30 +date: 2022-10-01 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 224a3cfeb7af..1d15139e5ad5 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 51c6ae605e7b..3fa079e3178f 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index c15bfc3b7fe2..ac6ccdaefbbf 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: 2022-09-30 +date: 2022-10-01 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 ea705da0da76..e8016cd77287 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: 2022-09-30 +date: 2022-10-01 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 0ea7624514ef..dff8ce771bde 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: 2022-09-30 +date: 2022-10-01 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 ac719690669b..318aa79a2975 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: 2022-09-30 +date: 2022-10-01 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 43a256fa418c..d29c21bc24d3 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -4254,6 +4254,53 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getUsedDataView", + "type": "Function", + "tags": [], + "label": "getUsedDataView", + "description": [], + "signature": [ + "((state: T, layerId: string) => string | undefined) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getUsedDataView.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getUsedDataView.$2", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.getUsedDataViews", @@ -4994,13 +5041,84 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getSupportedActionsForLayer", + "type": "Function", + "tags": [], + "label": "getSupportedActionsForLayer", + "description": [ + "\nreturns a list of custom actions supported by the visualization layer.\nDefault actions like delete/clear are not included in this list and are managed by the editor frame" + ], + "signature": [ + "((layerId: string, state: T, setState: ", + "StateSetter", + ") => ", + "LayerAction", + "[]) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getSupportedActionsForLayer.$1", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getSupportedActionsForLayer.$2", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getSupportedActionsForLayer.$3", + "type": "Function", + "tags": [], + "label": "setState", + "description": [], + "signature": [ + "StateSetter", + "" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.getLayerType", "type": "Function", "tags": [], "label": "getLayerType", - "description": [], + "description": [ + "returns the type string of the given layer" + ], "signature": [ "(layerId: string, state?: T | undefined) => ", { @@ -5494,6 +5612,43 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getDropProps", + "type": "Function", + "tags": [], + "label": "getDropProps", + "description": [], + "signature": [ + "((dropProps: ", + "GetDropPropsArgs", + ") => { dropTypes: ", + "DropType", + "[]; nextLabel?: string | undefined; } | undefined) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getDropProps.$1", + "type": "Object", + "tags": [], + "label": "dropProps", + "description": [], + "signature": [ + "GetDropPropsArgs", + "" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Visualization.renderDimensionEditor", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index ebeef2fc8c31..01c5bc3141bc 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 649 | 0 | 560 | 42 | +| 658 | 0 | 567 | 45 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 7a642bff9e73..e1894af2cbfa 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: 2022-09-30 +date: 2022-10-01 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 0f646773d613..d592aa08d875 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: 2022-09-30 +date: 2022-10-01 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 13e56db522d2..a65fb069d0ac 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 4a6747d66e72..e4e33df28af3 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 3b4067947d08..bcb5fc743472 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: 2022-09-30 +date: 2022-10-01 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 e68653a32c8c..e1409873d045 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: 2022-09-30 +date: 2022-10-01 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 8547850fa777..d6ef5a21773f 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index b52f48b93500..f725548e9cf9 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index eaa35953f39e..c72b46a487e9 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: 2022-09-30 +date: 2022-10-01 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 4048c3102a29..41785b90b47d 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 4b05d9ce3b7e..4d964c688c8a 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index b10f893f9601..d08449754434 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 758f400939d9..e40080c8ecf5 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 40c14dd0feeb..266faca28aed 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index faf5c1b11320..65ed265de39c 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 31986 | 179 | 21534 | 1003 | +| 32016 | 179 | 21562 | 1007 | ## Plugin Directory @@ -51,7 +51,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 60 | 0 | 30 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 966 | 0 | 208 | 1 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 983 | 0 | 225 | 2 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 28 | 3 | 24 | 1 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 10 | 0 | 8 | 2 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 97 | 0 | 80 | 4 | @@ -101,7 +101,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 418 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 649 | 0 | 560 | 42 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 658 | 0 | 567 | 45 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -153,10 +153,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 512 | 1 | 485 | 48 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 132 | 0 | 91 | 11 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 133 | 0 | 92 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 206 | 0 | 142 | 9 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 61 | 0 | 59 | 2 | -| | [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-services) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 125 | 2 | 99 | 17 | +| | [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-services) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 128 | 2 | 102 | 17 | | upgradeAssistant | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | urlDrilldown | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds drilldown implementations to Kibana | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 12 | 0 | 12 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 4d3613a8b117..581cd5ef4d51 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: 2022-09-30 +date: 2022-10-01 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 29db7898a4b5..817f03f981b1 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 49b546ef38a2..e074406c74e8 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: 2022-09-30 +date: 2022-10-01 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 18eff91aea12..5c1dc0c2685a 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: 2022-09-30 +date: 2022-10-01 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 099cff0ab518..df1bff571297 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: 2022-09-30 +date: 2022-10-01 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 c2aaaeb78852..ec349c4ad0b5 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: 2022-09-30 +date: 2022-10-01 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 5c28ac8b2bdb..5edda54c6f13 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: 2022-09-30 +date: 2022-10-01 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 6351e3d45b6e..d4d313bef79f 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: 2022-09-30 +date: 2022-10-01 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 b0955d53c8d0..1ae6118247ce 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: 2022-09-30 +date: 2022-10-01 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 ddb1f6d02750..3a2c36b7cc7f 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: 2022-09-30 +date: 2022-10-01 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 f9510b48f7ef..6e788653e193 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: 2022-09-30 +date: 2022-10-01 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 3239724d7ef3..1e81b07e6d24 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: 2022-09-30 +date: 2022-10-01 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 b11f668fa116..2146968cda90 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: 2022-09-30 +date: 2022-10-01 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 42b62ddb1d6d..d88e2e95c638 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: 2022-09-30 +date: 2022-10-01 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 2e8f47d4893b..dd4d7868a899 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index a576dc7f8b98..549878d171ab 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: 2022-09-30 +date: 2022-10-01 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 575793a05728..eac5df4aa58b 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 2ccc09799e6f..212526e3c021 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: 2022-09-30 +date: 2022-10-01 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 3c787d2f24b0..a3534fb4ba27 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index fcd7d2325c17..5c626675eef6 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: 2022-09-30 +date: 2022-10-01 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 a0c0f7d6c4c2..b8766114204c 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: 2022-09-30 +date: 2022-10-01 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 6c9a14514a8a..5bdce3da3e22 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: 2022-09-30 +date: 2022-10-01 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 fb2aab7cf00a..415c51b650dc 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: 2022-09-30 +date: 2022-10-01 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 5289e0a7dee5..e730f9b56148 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: 2022-09-30 +date: 2022-10-01 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 ec6a6fda9343..8cc947166e67 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: 2022-09-30 +date: 2022-10-01 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 28813ee2279e..7d0012a2281a 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: 2022-09-30 +date: 2022-10-01 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 167b467328b2..27fb11dfae9f 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: 2022-09-30 +date: 2022-10-01 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 c11efc4d4370..e358c37feb52 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: 2022-09-30 +date: 2022-10-01 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 8d9b53a137bc..54c0a5d93409 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: 2022-09-30 +date: 2022-10-01 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 5070df796c8a..9c00897d8c1f 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: 2022-09-30 +date: 2022-10-01 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 3115cbb40769..510d527d883c 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: 2022-09-30 +date: 2022-10-01 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 c7570c524590..c94cb7a1a751 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.devdocs.json b/api_docs/ui_actions.devdocs.json index 293aa53e4d80..d224e96cb9e8 100644 --- a/api_docs/ui_actions.devdocs.json +++ b/api_docs/ui_actions.devdocs.json @@ -2191,6 +2191,21 @@ "path": "src/plugins/ui_actions/public/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "uiActions", + "id": "def-public.VisualizeFieldContext.query", + "type": "CompoundType", + "tags": [], + "label": "query", + "description": [], + "signature": [ + "AggregateQuery", + " | undefined" + ], + "path": "src/plugins/ui_actions/public/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 9626982ef723..a67988ed1155 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 132 | 0 | 91 | 11 | +| 133 | 0 | 92 | 11 | ## Client diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 5e08f17c4b21..a2d9c4d869b7 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 2c376ec69d7e..ccea854bb073 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 8a490ca91dd2..5678db92f00c 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -11,7 +11,7 @@ "label": "DataViewPicker", "description": [], "signature": [ - "({ isMissingCurrent, currentDataViewId, adHocDataViews, onChangeDataView, onAddField, onDataViewCreated, trigger, selectableProps, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, isDisabled, }: ", + "({ isMissingCurrent, currentDataViewId, adHocDataViews, onChangeDataView, onAddField, onDataViewCreated, trigger, selectableProps, textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, textBasedLanguage, onCreateDefaultAdHocDataView, isDisabled, }: ", "DataViewPickerPropsExtended", ") => JSX.Element" ], @@ -24,7 +24,7 @@ "id": "def-public.DataViewPicker.$1", "type": "Object", "tags": [], - "label": "{\n isMissingCurrent,\n currentDataViewId,\n adHocDataViews,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n isDisabled,\n}", + "label": "{\n isMissingCurrent,\n currentDataViewId,\n adHocDataViews,\n onChangeDataView,\n onAddField,\n onDataViewCreated,\n trigger,\n selectableProps,\n textBasedLanguages,\n onSaveTextLanguageQuery,\n onTextLangQuerySubmit,\n textBasedLanguage,\n onCreateDefaultAdHocDataView,\n isDisabled,\n}", "description": [], "signature": [ "DataViewPickerPropsExtended" @@ -645,6 +645,38 @@ "children": [], "returnComment": [] }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.DataViewPickerProps.onCreateDefaultAdHocDataView", + "type": "Function", + "tags": [], + "label": "onCreateDefaultAdHocDataView", + "description": [], + "signature": [ + "((pattern: string) => void) | undefined" + ], + "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.DataViewPickerProps.onCreateDefaultAdHocDataView.$1", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/unified_search/public/dataview_picker/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "unifiedSearch", "id": "def-public.DataViewPickerProps.textBasedLanguages", @@ -1058,6 +1090,26 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.IUnifiedSearchPluginServices.dataViews", + "type": "Object", + "tags": [], + "label": "dataViews", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "public", + "docId": "kibDataViewsPluginApi", + "section": "def-public.DataViewsServicePublic", + "text": "DataViewsServicePublic" + } + ], + "path": "src/plugins/unified_search/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "unifiedSearch", "id": "def-public.IUnifiedSearchPluginServices.usageCollection", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index de104e703fd9..76011654fbea 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 125 | 2 | 99 | 17 | +| 128 | 2 | 102 | 17 | ## Client diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 6138b56184d2..14a0d57226b4 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 125 | 2 | 99 | 17 | +| 128 | 2 | 102 | 17 | ## Client diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index e44699c7b7c5..f7410808300e 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: 2022-09-30 +date: 2022-10-01 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 b59d6dcec268..a62461b88cee 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: 2022-09-30 +date: 2022-10-01 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 b5e23e55dfb6..7d7a4df32df8 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: 2022-09-30 +date: 2022-10-01 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 2fb7351aa02f..3a1a53675dee 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: 2022-09-30 +date: 2022-10-01 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 408e2c2ca376..cfdaf08f6cc1 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: 2022-09-30 +date: 2022-10-01 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 fa44a39b582b..b26a487e9cbb 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: 2022-09-30 +date: 2022-10-01 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 cd176ecf49a9..a3aaeb805b0c 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: 2022-09-30 +date: 2022-10-01 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 d5b90e601d8e..aff984617861 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: 2022-09-30 +date: 2022-10-01 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 ba692d609d71..c1f44c36c05d 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: 2022-09-30 +date: 2022-10-01 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 4b7469040671..179e2273cf67 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: 2022-09-30 +date: 2022-10-01 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 9eddbfeec41d..691934f5c9b1 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: 2022-09-30 +date: 2022-10-01 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 31e138d68aa1..a18309726008 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: 2022-09-30 +date: 2022-10-01 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 a1d6cb915412..9733a2e51f76 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index db658a8b5a0b..1b87bc4d3156 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: 2022-09-30 +date: 2022-10-01 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From e50b5eb6da88b426bf4eebd02e3c535c92ab6c34 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Sat, 1 Oct 2022 07:20:13 -0400 Subject: [PATCH 178/185] Allow custom sources to have control over data sync (#141829) --- .../raster_tile_layer.test.ts | 17 ++++++-- .../raster_tile_layer/raster_tile_layer.ts | 43 ++++++------------- .../kibana_tilemap_source.js | 10 ++++- .../classes/sources/raster_source/index.ts | 18 ++++++++ .../classes/sources/tms_source/index.ts | 1 - .../classes/sources/wms_source/wms_source.js | 10 ++++- .../sources/xyz_tms_source/xyz_tms_source.ts | 38 ++++++++++++++-- .../maps/public/selectors/map_selectors.ts | 4 +- 8 files changed, 101 insertions(+), 40 deletions(-) create mode 100644 x-pack/plugins/maps/public/classes/sources/raster_source/index.ts diff --git a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts index 963a12e9f737..c42b338032f0 100644 --- a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.test.ts @@ -7,10 +7,12 @@ import { RasterTileLayer } from './raster_tile_layer'; import { SOURCE_TYPES } from '../../../../common/constants'; -import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; +import { DataRequestMeta, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; import { AbstractSource } from '../../sources/source'; -import { ITMSSource } from '../../sources/tms_source'; import { ILayer } from '../layer'; +import { RasterTileSource } from 'maplibre-gl'; +import { DataRequest } from '../../util/data_request'; +import { IRasterSource, RasterTileSourceData } from '../../sources/raster_source'; const sourceDescriptor: XYZTMSSourceDescriptor = { type: SOURCE_TYPES.EMS_XYZ, @@ -18,12 +20,21 @@ const sourceDescriptor: XYZTMSSourceDescriptor = { id: 'foobar', }; -class MockTileSource extends AbstractSource implements ITMSSource { +class MockTileSource extends AbstractSource implements IRasterSource { readonly _descriptor: XYZTMSSourceDescriptor; constructor(descriptor: XYZTMSSourceDescriptor) { super(descriptor); this._descriptor = descriptor; } + async canSkipSourceUpdate( + dataRequest: DataRequest, + nextRequestMeta: DataRequestMeta + ): Promise { + return true; + } + isSourceStale(mbSource: RasterTileSource, sourceData: RasterTileSourceData): boolean { + return false; + } async getDisplayName(): Promise { return this._descriptor.urlTemplate; diff --git a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts index fc471375e1d3..cc13b70d0106 100644 --- a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts @@ -9,15 +9,11 @@ import type { Map as MbMap, RasterTileSource } from '@kbn/mapbox-gl'; import _ from 'lodash'; import { AbstractLayer } from '../layer'; import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE, LAYER_STYLE_TYPE } from '../../../../common/constants'; -import { LayerDescriptor, Timeslice } from '../../../../common/descriptor_types'; +import { LayerDescriptor } from '../../../../common/descriptor_types'; import { TileStyle } from '../../styles/tile/tile_style'; -import { ITMSSource } from '../../sources/tms_source'; import { DataRequestContext } from '../../../actions'; -import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; -interface RasterTileSourceData { - url: string; -} +import { IRasterSource, RasterTileSourceData } from '../../sources/raster_source'; export class RasterTileLayer extends AbstractLayer { static createDescriptor(options: Partial) { @@ -34,15 +30,15 @@ export class RasterTileLayer extends AbstractLayer { source, layerDescriptor, }: { - source: ITMSSource; + source: IRasterSource; layerDescriptor: LayerDescriptor; }) { super({ source, layerDescriptor }); this._style = new TileStyle(); } - getSource(): ITMSSource { - return super.getSource() as ITMSSource; + getSource(): IRasterSource { + return super.getSource() as IRasterSource; } getStyleForEditing() { @@ -65,17 +61,7 @@ export class RasterTileLayer extends AbstractLayer { }; const prevDataRequest = this.getSourceDataRequest(); if (prevDataRequest) { - const prevMeta = prevDataRequest?.getMeta(); - const canSkip = await canSkipSourceUpdate({ - extentAware: false, - source, - prevDataRequest, - nextRequestMeta: nextMeta, - getUpdateDueToTimeslice: (timeslice?: Timeslice) => { - if (!prevMeta) return true; - return source.getUpdateDueToTimeslice(prevMeta, timeslice); - }, - }); + const canSkip = await source.canSkipSourceUpdate(prevDataRequest, nextMeta); if (canSkip) return; } const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`); @@ -107,21 +93,20 @@ export class RasterTileLayer extends AbstractLayer { } _requiresPrevSourceCleanup(mbMap: MbMap): boolean { + const source = this.getSource(); const mbSource = mbMap.getSource(this.getMbSourceId()) as RasterTileSource; if (!mbSource) { return false; } const sourceDataRequest = this.getSourceDataRequest(); - if (!sourceDataRequest) { - return false; - } - const sourceData = sourceDataRequest.getData() as RasterTileSourceData | undefined; - if (!sourceData) { - return false; + if (sourceDataRequest) { + const data = sourceDataRequest.getData(); + if (data) { + return source.isSourceStale(mbSource, data as RasterTileSourceData); + } } - - return mbSource.tiles?.[0] !== sourceData.url; + return false; } syncLayerWithMB(mbMap: MbMap) { @@ -138,7 +123,7 @@ export class RasterTileLayer extends AbstractLayer { return; } - const tmsSourceData = sourceDataRequest.getData() as { url?: string }; + const tmsSourceData = sourceDataRequest.getData() as RasterTileSourceData; if (!tmsSourceData || !tmsSourceData.url) { return; } diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js index db0b5359ca56..19a7ec294110 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js +++ b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js @@ -41,7 +41,15 @@ export class KibanaTilemapSource extends AbstractSource { }, ]; } - + isSourceStale(mbSource, sourceData) { + if (!sourceData.url) { + return false; + } + return mbSource.tiles?.[0] !== sourceData.url; + } + async canSkipSourceUpdate() { + return false; + } async getUrlTemplate() { const tilemap = getKibanaTileMap(); if (!tilemap.url) { diff --git a/x-pack/plugins/maps/public/classes/sources/raster_source/index.ts b/x-pack/plugins/maps/public/classes/sources/raster_source/index.ts new file mode 100644 index 000000000000..53f1b75003ea --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/raster_source/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RasterTileSource } from '@kbn/mapbox-gl'; +import { DataRequest } from '../../util/data_request'; +import { ITMSSource } from '../tms_source'; +import { DataRequestMeta } from '../../../../common/descriptor_types'; +export interface RasterTileSourceData { + url: string; +} +export interface IRasterSource extends ITMSSource { + canSkipSourceUpdate(dataRequest: DataRequest, nextRequestMeta: DataRequestMeta): Promise; + isSourceStale(mbSource: RasterTileSource, sourceData: RasterTileSourceData): boolean; +} diff --git a/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts b/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts index 05207acf7329..d18a00df34f0 100644 --- a/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts @@ -7,7 +7,6 @@ import { DataFilters } from '../../../../common/descriptor_types'; import { ISource } from '../source'; - export interface ITMSSource extends ISource { getUrlTemplate(dataFilters: DataFilters): Promise; } diff --git a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js index b884785d348a..a1c1c60d7556 100644 --- a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js @@ -27,7 +27,15 @@ export class WMSSource extends AbstractSource { styles, }; } - + isSourceStale(mbSource, sourceData) { + if (!sourceData.url) { + return false; + } + return mbSource.tiles?.[0] !== sourceData.url; + } + async canSkipSourceUpdate() { + return false; + } async getImmutableProperties() { return [ { label: getDataSourceLabel(), value: sourceTitle }, diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts index 01f77e4a45c3..c2c5e6404c8f 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts @@ -6,19 +6,26 @@ */ import { i18n } from '@kbn/i18n'; +import { RasterTileSource } from 'maplibre-gl'; import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters'; import { SOURCE_TYPES } from '../../../../common/constants'; import { registerSource } from '../source_registry'; -import { ITMSSource } from '../tms_source'; -import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; +import { + XYZTMSSourceDescriptor, + DataRequestMeta, + Timeslice, +} from '../../../../common/descriptor_types'; import { AbstractSource, ImmutableSourceProperty } from '../source'; import { XYZTMSSourceConfig } from './xyz_tms_editor'; +import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; +import { DataRequest } from '../../util/data_request'; +import { IRasterSource, RasterTileSourceData } from '../raster_source'; export const sourceTitle = i18n.translate('xpack.maps.source.ems_xyzTitle', { defaultMessage: 'Tile Map Service', }); -export class XYZTMSSource extends AbstractSource implements ITMSSource { +export class XYZTMSSource extends AbstractSource implements IRasterSource { static type = SOURCE_TYPES.EMS_XYZ; readonly _descriptor: XYZTMSSourceDescriptor; @@ -49,6 +56,31 @@ export class XYZTMSSource extends AbstractSource implements ITMSSource { async getUrlTemplate(): Promise { return this._descriptor.urlTemplate; } + + isSourceStale(mbSource: RasterTileSource, sourceData: RasterTileSourceData): boolean { + if (!sourceData.url) { + return false; + } + return mbSource.tiles?.[0] !== sourceData.url; + } + + async canSkipSourceUpdate( + prevDataRequest: DataRequest, + nextMeta: DataRequestMeta + ): Promise { + const prevMeta = prevDataRequest?.getMeta(); + const canSkip = await canSkipSourceUpdate({ + extentAware: false, + source: this, + prevDataRequest, + nextRequestMeta: nextMeta, + getUpdateDueToTimeslice: (timeslice?: Timeslice) => { + if (!prevMeta) return true; + return this.getUpdateDueToTimeslice(prevMeta, timeslice); + }, + }); + return canSkip; + } } registerSource({ diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 8adb3f8f927a..6ee55bd72e49 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -54,10 +54,10 @@ import { VectorLayerDescriptor, } from '../../common/descriptor_types'; import { ISource } from '../classes/sources/source'; -import { ITMSSource } from '../classes/sources/tms_source'; import { IVectorSource } from '../classes/sources/vector_source'; import { ESGeoGridSource } from '../classes/sources/es_geo_grid_source'; import { EMSTMSSource } from '../classes/sources/ems_tms_source'; +import { IRasterSource } from '../classes/sources/raster_source'; import { ILayer } from '../classes/layers/layer'; import { getIsReadOnly } from './ui_selectors'; @@ -78,7 +78,7 @@ export function createLayerInstance( switch (layerDescriptor.type) { case LAYER_TYPE.RASTER_TILE: - return new RasterTileLayer({ layerDescriptor, source: source as ITMSSource }); + return new RasterTileLayer({ layerDescriptor, source: source as IRasterSource }); case LAYER_TYPE.EMS_VECTOR_TILE: return new EmsVectorTileLayer({ layerDescriptor: layerDescriptor as EMSVectorTileLayerDescriptor, From 083b9a74fd6e291c1113acd8ef996a940cd5b1a3 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:38:50 -0600 Subject: [PATCH 179/185] [api-docs] Daily api_docs build (#142425) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_common.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- api_docs/kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- api_docs/kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- api_docs/kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- api_docs/kbn_core_application_browser_internal.mdx | 2 +- api_docs/kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- api_docs/kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- api_docs/kbn_core_deprecations_browser_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- api_docs/kbn_core_deprecations_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_mocks.mdx | 2 +- api_docs/kbn_core_environment_server_internal.mdx | 2 +- api_docs/kbn_core_environment_server_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_browser.mdx | 2 +- api_docs/kbn_core_execution_context_browser_internal.mdx | 2 +- api_docs/kbn_core_execution_context_browser_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_common.mdx | 2 +- api_docs/kbn_core_execution_context_server.mdx | 2 +- api_docs/kbn_core_execution_context_server_internal.mdx | 2 +- api_docs/kbn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- api_docs/kbn_core_http_context_server_mocks.mdx | 2 +- api_docs/kbn_core_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_router_server_internal.mdx | 2 +- api_docs/kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- api_docs/kbn_core_injected_metadata_browser.mdx | 2 +- api_docs/kbn_core_injected_metadata_browser_mocks.mdx | 2 +- api_docs/kbn_core_integrations_browser_internal.mdx | 2 +- api_docs/kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- api_docs/kbn_core_notifications_browser_internal.mdx | 2 +- api_docs/kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- api_docs/kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_api_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- .../kbn_core_saved_objects_import_export_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- api_docs/kbn_core_saved_objects_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- api_docs/kbn_core_test_helpers_deprecations_getters.mdx | 2 +- api_docs/kbn_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- api_docs/kbn_core_ui_settings_server_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- api_docs/kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- api_docs/kbn_securitysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_alerting_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- api_docs/kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_user_profile_components.mdx | 2 +- api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 404 files changed, 404 insertions(+), 404 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 48d75bc3ce7c..f91e6ca14c5c 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: 2022-10-01 +date: 2022-10-02 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 ee9b4276cce6..7c38365d9afe 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 9d860aa24355..1adc1287e6f2 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: 2022-10-01 +date: 2022-10-02 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 e72baca86cbf..3da854ba53f1 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: 2022-10-01 +date: 2022-10-02 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 020843029e21..5831360d8a8c 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index b792f664c347..a25e8c846c7e 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: 2022-10-01 +date: 2022-10-02 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 a343745fd134..06dd79899f4f 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: 2022-10-01 +date: 2022-10-02 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 74326b5f4e70..852476836414 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: 2022-10-01 +date: 2022-10-02 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 6d8fcf94ddb8..aed43e616a03 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: 2022-10-01 +date: 2022-10-02 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 f2eb1e6d668c..166c25203c17 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: 2022-10-01 +date: 2022-10-02 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 a7f1c3bd483c..4cf122ec44f1 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 4cd830231b26..afce96b1debd 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 7afab612c4a7..6da76742d8c2 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: 2022-10-01 +date: 2022-10-02 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 1ace57ec36bd..c5b317f06f24 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 1e2f85a3b6c6..7af97619b1a3 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.mdx b/api_docs/core.mdx index e3d2f1354aea..5f59293c8f37 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 62deee07bd7d..6778fc3543db 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: 2022-10-01 +date: 2022-10-02 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 abecd96470ec..711da58211c5 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: 2022-10-01 +date: 2022-10-02 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 6a49051f2936..a4aac32db170 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: 2022-10-01 +date: 2022-10-02 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 44fc64c6bdf4..74a573292d79 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index a82d817ae131..52ec376fb7cb 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: 2022-10-01 +date: 2022-10-02 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 3e7214a59cc2..c0cda63f6162 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 288b3ee585f5..3a448dea572e 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: 2022-10-01 +date: 2022-10-02 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 f0c7b8022d14..c4933b68c936 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: 2022-10-01 +date: 2022-10-02 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 9840337f75a7..35b2849b1521 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: 2022-10-01 +date: 2022-10-02 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 9ce3b6c70063..8b66e010c36d 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: 2022-10-01 +date: 2022-10-02 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 05d260b1c544..17ed535ee87c 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index a3d96055c821..f4b1fa55c1ed 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 4eb2eb95d80e..0c5bbd60e517 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 4fc141f38747..60d74fa28721 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index f22d72807964..06dfec816d60 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: 2022-10-01 +date: 2022-10-02 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 c744310a34b9..e703c38627cb 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: 2022-10-01 +date: 2022-10-02 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 b9b4af0426fd..591663448e85 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index f09224c58150..9782e32b7985 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: 2022-10-01 +date: 2022-10-02 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 e1530fd32233..84dd77d22a23 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: 2022-10-01 +date: 2022-10-02 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 3f0a323b9279..ffd479100976 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: 2022-10-01 +date: 2022-10-02 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 68f9252a936a..6d8e9e20d699 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index e4b29fe30fce..661382fdf519 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 5fc5dd479579..d752e0f31cc9 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index a1aaa7507ccb..cfa039d75aa8 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index a7413684bd03..1ea659904646 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: 2022-10-01 +date: 2022-10-02 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 a882614c990a..2e6be367a1d9 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: 2022-10-01 +date: 2022-10-02 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 9237719c9d20..f4086885e507 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: 2022-10-01 +date: 2022-10-02 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 aae35271ff39..b0d7b0b0e664 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: 2022-10-01 +date: 2022-10-02 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 df673151690c..f58979b777a3 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: 2022-10-01 +date: 2022-10-02 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 da11f3077c1a..1b1035d10c9e 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: 2022-10-01 +date: 2022-10-02 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 846aa497ea3b..8c3a8f9b3933 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: 2022-10-01 +date: 2022-10-02 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 02618661f080..646de5a02517 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: 2022-10-01 +date: 2022-10-02 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 02f01b4dd86f..4913f9da7c39 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: 2022-10-01 +date: 2022-10-02 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 acd06327d888..906b0f9a865c 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: 2022-10-01 +date: 2022-10-02 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 8046b493fef2..32e6eca8a079 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: 2022-10-01 +date: 2022-10-02 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 3ddfd870314a..7eb965182927 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 0b5d88e7aab7..c727f32752b6 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 8c0901015111..63b7d486fa20 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: 2022-10-01 +date: 2022-10-02 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 05df2ffc3a48..14e361da2246 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: 2022-10-01 +date: 2022-10-02 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 fb46fbf3c533..4c8ac407c2a2 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index ad24537aa0fd..a5923aea7ee0 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: 2022-10-01 +date: 2022-10-02 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 77286018c623..d40d7e303d6b 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 8196936c8efd..bafd3ddd8518 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 6908d7194d17..42378d31281f 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: 2022-10-01 +date: 2022-10-02 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 af5a128f54b0..d183f51fa149 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: 2022-10-01 +date: 2022-10-02 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 8bbe725e782f..388cdf51fff4 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 9edd7ca01eff..36f4a84d6d37 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: 2022-10-01 +date: 2022-10-02 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 0c3c64c38fa9..f142e5206790 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 12e3a5944bfe..ba44a8375901 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index d5d9c2b4b693..cebd40add541 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 3f156bfcb7b3..bcfc55b7a310 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 9033fdf982ef..de04343d39dd 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index dae5a070183a..640d8bc0dadd 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 25f9c2c92bfd..37748e5923d7 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index ca9ca177b00a..4f93c597b2e4 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 9c5c4346e45a..653e93710bb2 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 0f94bac6c9c8..ab8ade876b3b 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 71847c01b1eb..94415cdc496c 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 09e9cdc683fa..bc6057bd6596 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 14753eade649..953ad7736eaa 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 7b98319dfa0d..68b4f9a89cc3 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 7e89a7719619..633a769efa1a 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: 2022-10-01 +date: 2022-10-02 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index a8302426d0f9..99ce7b8a19bb 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 8e3217250a2f..962b5aa586a1 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index b96fac650f3b..50f8cd64aee1 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 2e27eedb97be..476bfef859d0 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: 2022-10-01 +date: 2022-10-02 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 ba566208fe8b..549084cde27d 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: 2022-10-01 +date: 2022-10-02 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 71b3793b5edb..2256eb2966ed 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: 2022-10-01 +date: 2022-10-02 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 b2e6e11bf6f5..ad42b9ac3ee4 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: 2022-10-01 +date: 2022-10-02 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 741e1c8d09dc..22a6845fb05d 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 534685b36cd2..2d54eb64e603 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: 2022-10-01 +date: 2022-10-02 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 3ff3d642b506..a52202f78637 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: 2022-10-01 +date: 2022-10-02 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 67fdb94f78f3..192f0654b7d5 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: 2022-10-01 +date: 2022-10-02 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 3b43b92a5565..5633664daf6d 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 84ad21fc47fd..bbf2c310a588 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 8c307425ee04..e090ff0a2674 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: 2022-10-01 +date: 2022-10-02 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 3212fb673e27..fe43394f77fb 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: 2022-10-01 +date: 2022-10-02 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 4e9dd3d54d35..e9c9b866f0fe 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: 2022-10-01 +date: 2022-10-02 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 7bd027365d6e..a86d4bce5628 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: 2022-10-01 +date: 2022-10-02 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 76cd9dba2db5..9b2fe9f9f192 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: 2022-10-01 +date: 2022-10-02 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 bfa75dd3afd7..c2dc15b657d7 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: 2022-10-01 +date: 2022-10-02 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 7f9da63f298a..e935da728930 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: 2022-10-01 +date: 2022-10-02 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 2f2615a48955..2a05ae0c01c5 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: 2022-10-01 +date: 2022-10-02 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 9c806330e366..2e74725d2f3d 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: 2022-10-01 +date: 2022-10-02 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 9294b5b708f8..4a541f590e2f 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: 2022-10-01 +date: 2022-10-02 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 af582f9ff198..3463e8e9384e 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: 2022-10-01 +date: 2022-10-02 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 8af8f902e540..834faf66c67a 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: 2022-10-01 +date: 2022-10-02 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index aaf8373e9ca7..f22445d1e10e 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: 2022-10-01 +date: 2022-10-02 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 9ed7dbaa5e2d..fe429f0f502a 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: 2022-10-01 +date: 2022-10-02 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 8186b42e0220..9d162ecc81bb 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: 2022-10-01 +date: 2022-10-02 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 a0c063e84009..be46292c9bc9 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: 2022-10-01 +date: 2022-10-02 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 221ae3385ddd..aa7f5b3d5df7 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: 2022-10-01 +date: 2022-10-02 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 2156646c76f0..1380bd04beea 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: 2022-10-01 +date: 2022-10-02 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 f6c8668e76f2..00087d6131cc 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: 2022-10-01 +date: 2022-10-02 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 3d39b753f2ab..e4e5a9223ba0 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index 6e6ffeac0f65..251b2d507b16 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: 2022-10-01 +date: 2022-10-02 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 203f9a365fde..10c16f282a6f 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: 2022-10-01 +date: 2022-10-02 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 cd573bfd8776..8d29eb118d55 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: 2022-10-01 +date: 2022-10-02 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index e2134b925ba8..0d64e581664e 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: 2022-10-01 +date: 2022-10-02 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 9fc96d5d598f..6d31a54a5605 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: 2022-10-01 +date: 2022-10-02 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 7a6634ac7f8d..9c7aa1019372 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index edcf87b96094..bc77cf44b3bd 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 35e8a6ce2632..a976ebcc9a0e 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: 2022-10-01 +date: 2022-10-02 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 8167097daa0d..a3af9d6298c5 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: 2022-10-01 +date: 2022-10-02 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 b45ecd8257b0..42feea3f29fb 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: 2022-10-01 +date: 2022-10-02 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 031456455b1e..1d24379f441f 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: 2022-10-01 +date: 2022-10-02 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 381310fcf154..48c6d6862f4d 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: 2022-10-01 +date: 2022-10-02 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 05f768b81b3c..8195d4f62389 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: 2022-10-01 +date: 2022-10-02 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 52776680286a..85fa2c471574 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: 2022-10-01 +date: 2022-10-02 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 63cc80d8fa73..feabe3a92d7f 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: 2022-10-01 +date: 2022-10-02 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 309f4ef8839d..7b9875f589c6 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: 2022-10-01 +date: 2022-10-02 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 d4df75707a94..1e0c6217b952 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: 2022-10-01 +date: 2022-10-02 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 a5ade116a7c0..7bc107af9a80 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: 2022-10-01 +date: 2022-10-02 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 9a09fe50975c..95497dfe20dc 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: 2022-10-01 +date: 2022-10-02 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 7fffb0ccbd3d..5f7c6dc637c8 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: 2022-10-01 +date: 2022-10-02 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 7c8733b369f0..bbda34217fa7 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: 2022-10-01 +date: 2022-10-02 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 f283e05e563d..fad63abe1993 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: 2022-10-01 +date: 2022-10-02 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 d40d5501ca99..006d8b447663 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: 2022-10-01 +date: 2022-10-02 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 cc1a420ccba1..96e88c7e5c2a 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: 2022-10-01 +date: 2022-10-02 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 25f0346340ea..7e3559438efa 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: 2022-10-01 +date: 2022-10-02 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 87eb2c5dd2fd..e6ab7c3d8915 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: 2022-10-01 +date: 2022-10-02 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 1023c4c175a3..17fee1daced5 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: 2022-10-01 +date: 2022-10-02 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 20aa61f141c8..ed465631b22b 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: 2022-10-01 +date: 2022-10-02 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 4fdaf9d97f6f..89eb21cdb4d5 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: 2022-10-01 +date: 2022-10-02 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 81e62c8036e0..3e1fbaec76d3 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: 2022-10-01 +date: 2022-10-02 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 548174fbd5d8..13817b60a15a 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: 2022-10-01 +date: 2022-10-02 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 16c6d6b51020..3380e7e1a2b4 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: 2022-10-01 +date: 2022-10-02 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 094479784c51..4020a1edd3ae 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: 2022-10-01 +date: 2022-10-02 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 41f882912cfe..a48229db2238 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: 2022-10-01 +date: 2022-10-02 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 b00edeb7d68b..6c86cd7218b8 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: 2022-10-01 +date: 2022-10-02 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 17d48963b8b7..a41bd0793732 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: 2022-10-01 +date: 2022-10-02 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 2ee35b66a8f6..98354a9107f3 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 750d6c7b3bef..99ac50e292b6 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_http_server.mdx index 3a9e92dbe650..fd022dfb3eea 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 27352519951f..f058d1e28dec 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 2aa52e0dc1c0..1b6cf98922c1 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: 2022-10-01 +date: 2022-10-02 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 a8e82e67d22c..bc10c115fb75 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: 2022-10-01 +date: 2022-10-02 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 10be37f40c29..02d199810fef 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: 2022-10-01 +date: 2022-10-02 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 ef2963239008..b391482c1e67 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: 2022-10-01 +date: 2022-10-02 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 d6ed176629a2..8bdf305b1964 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: 2022-10-01 +date: 2022-10-02 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 ee1ed897ede0..949a0fde56bc 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index 7b120559dc17..f93ad919d9fe 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 3e7af178ec12..6389c0fa21a8 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: 2022-10-01 +date: 2022-10-02 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 d2aabf046c88..f1c6bacc8a19 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: 2022-10-01 +date: 2022-10-02 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 bc12a2785cc0..91fca25dd497 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: 2022-10-01 +date: 2022-10-02 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 7480b362f29b..8aee8a80734c 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: 2022-10-01 +date: 2022-10-02 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 f21296396e1d..d76de30044e4 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: 2022-10-01 +date: 2022-10-02 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 61be5ea720a4..575ad511b8fd 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: 2022-10-01 +date: 2022-10-02 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 af6dd35e8312..02793dca4902 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: 2022-10-01 +date: 2022-10-02 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 a09ca7932e57..259d3fd65a26 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: 2022-10-01 +date: 2022-10-02 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 c01ad905bc16..81710e21c33f 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: 2022-10-01 +date: 2022-10-02 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 d31c4dd48ee5..e3e171db8439 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: 2022-10-01 +date: 2022-10-02 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 9f15f6cf7390..bacd5044596d 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: 2022-10-01 +date: 2022-10-02 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 e93ec0476f25..eadb43389a16 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: 2022-10-01 +date: 2022-10-02 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 5a94cd10fcb0..baaf23f8822d 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: 2022-10-01 +date: 2022-10-02 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 43e64914cfb3..0b68bd525a64 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: 2022-10-01 +date: 2022-10-02 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 557e650a73b5..08f701606253 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: 2022-10-01 +date: 2022-10-02 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 a3ebabf7f32c..5b065122b113 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: 2022-10-01 +date: 2022-10-02 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 804d680bae80..e540991a1252 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: 2022-10-01 +date: 2022-10-02 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 e15645fb9ab8..8bea394dae01 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: 2022-10-01 +date: 2022-10-02 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 a64c369bcba4..ad8e17b446e9 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: 2022-10-01 +date: 2022-10-02 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 1192b6244a3a..514474ea2ef2 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: 2022-10-01 +date: 2022-10-02 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 0eb8bc161976..bae3dbcf6325 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: 2022-10-01 +date: 2022-10-02 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 d8b0afcc889b..054489949922 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: 2022-10-01 +date: 2022-10-02 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 7982df2fb243..9b739d1209d1 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: 2022-10-01 +date: 2022-10-02 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 9c70f02f78d0..5b9a435bacaf 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: 2022-10-01 +date: 2022-10-02 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 6bd600cfb198..b9fa84b19f8c 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: 2022-10-01 +date: 2022-10-02 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index be27903d8b48..0906ee4ed838 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: 2022-10-01 +date: 2022-10-02 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 b4745db58332..6b97c1f88067 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: 2022-10-01 +date: 2022-10-02 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 47752b2ae672..520e18ebb3e7 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: 2022-10-01 +date: 2022-10-02 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 856ab9b0e54b..020fabbec183 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: 2022-10-01 +date: 2022-10-02 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 79709ae89848..6da27ef12143 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: 2022-10-01 +date: 2022-10-02 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index b88c15571412..df6fc60584f4 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 f28c4920181c..81a29c9d9a72 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: 2022-10-01 +date: 2022-10-02 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 2bf85267549a..b2eb85135b8d 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: 2022-10-01 +date: 2022-10-02 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 3f364ace01fd..d6eb21156cef 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: 2022-10-01 +date: 2022-10-02 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 2b9b805aa0d5..c0db5893ecdc 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: 2022-10-01 +date: 2022-10-02 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 94ee2b5e95dd..6b1df9e60f5a 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: 2022-10-01 +date: 2022-10-02 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 f2e58e7919b5..3767a975778f 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: 2022-10-01 +date: 2022-10-02 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 2f9686598dae..46e635afccff 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: 2022-10-01 +date: 2022-10-02 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 67a62a9d8f82..d9dcf279b04e 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: 2022-10-01 +date: 2022-10-02 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 f006d43902df..b273522ddd59 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: 2022-10-01 +date: 2022-10-02 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 962c7d5ac011..4a63a5bd89ef 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: 2022-10-01 +date: 2022-10-02 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 c07112e6ad54..c02fc259a89b 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: 2022-10-01 +date: 2022-10-02 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 40a150c47507..78b702fb4478 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: 2022-10-01 +date: 2022-10-02 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 9d63c473addd..90c8b849e0ba 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: 2022-10-01 +date: 2022-10-02 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 27238b71e947..f349189ab730 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: 2022-10-01 +date: 2022-10-02 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 6ca747830ab4..8c5aeb1e8262 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: 2022-10-01 +date: 2022-10-02 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 768e628a8af9..d9566708b3ae 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: 2022-10-01 +date: 2022-10-02 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 970cb7ab087e..a55470733390 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: 2022-10-01 +date: 2022-10-02 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 cd7bbe51e904..439d25504386 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: 2022-10-01 +date: 2022-10-02 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 da072c5533de..96f568f178a4 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: 2022-10-01 +date: 2022-10-02 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 0a9599ba8cac..85b8475863cf 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: 2022-10-01 +date: 2022-10-02 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 57940d8a2498..957576c2e148 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: 2022-10-01 +date: 2022-10-02 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 ba62b2164435..aeaf0b8ee110 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: 2022-10-01 +date: 2022-10-02 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index c3939a291a65..45aa25f3d172 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: 2022-10-01 +date: 2022-10-02 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 6148fce41049..4b3a42ce6cc7 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 5fb0272483d5..92f080e0f528 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: 2022-10-01 +date: 2022-10-02 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 28de861c3382..4a1529581735 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: 2022-10-01 +date: 2022-10-02 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 d9a6c2b14d25..6fb6935573a7 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: 2022-10-01 +date: 2022-10-02 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 3dd7677225f0..31697c023dda 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: 2022-10-01 +date: 2022-10-02 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 8a73e4407035..574d0ec8e7b4 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: 2022-10-01 +date: 2022-10-02 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 b86978a4c0f5..0997bcdc2211 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: 2022-10-01 +date: 2022-10-02 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 632bc077b3e1..7b4df9498b90 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: 2022-10-01 +date: 2022-10-02 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 43ce870fd9d1..d222af41c3fd 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index 982847f0d737..976fc475761a 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 55d78bd6eee1..9bd4dab752ef 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: 2022-10-01 +date: 2022-10-02 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 4a0c5588f0be..8dd7253ab6ab 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: 2022-10-01 +date: 2022-10-02 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_crypto.mdx b/api_docs/kbn_crypto.mdx index 521e5b631e39..a8049f93e042 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: 2022-10-01 +date: 2022-10-02 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 58ddc73e08df..d5209c039a16 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 644ca142bc7d..76052024f029 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 61773de18ba4..39e11f8614d8 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: 2022-10-01 +date: 2022-10-02 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 33f29af6449f..95871faf8678 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: 2022-10-01 +date: 2022-10-02 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 7160587ae7c0..198147e7c822 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: 2022-10-01 +date: 2022-10-02 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 765e362ac500..b9922d230eb7 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index d74e0af0ba44..3fe12c91c32a 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: 2022-10-01 +date: 2022-10-02 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 bd64af2bd9d2..3545dbecd696 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 16b1791d70aa..b4270c9011bb 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 516930bec71f..ca423dde4451 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: 2022-10-01 +date: 2022-10-02 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 f8cfd1377e56..f422e71b9a8b 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: 2022-10-01 +date: 2022-10-02 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 0125789a530a..a0bf853e54fd 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: 2022-10-01 +date: 2022-10-02 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 faa1089fab65..42124a156bff 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: 2022-10-01 +date: 2022-10-02 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 756b802d0c32..883203cc6f10 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index bb82f2d25977..8791798c6465 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index f745989c9c8a..ae1139f5d360 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: 2022-10-01 +date: 2022-10-02 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 6ddc526b2548..1c2551073eb2 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: 2022-10-01 +date: 2022-10-02 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_generate.mdx b/api_docs/kbn_generate.mdx index 42e063e32e7f..9e670335e3a9 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 984eab3d218e..816ea84cc969 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 0fcc343e3d19..88db861974d6 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: 2022-10-01 +date: 2022-10-02 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 2b73efa6062a..be13032975df 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 0dc1d0876d3a..71c8b52c08c7 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: 2022-10-01 +date: 2022-10-02 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 f8639118d81e..67b69bdc6937 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: 2022-10-01 +date: 2022-10-02 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 78d78be33902..287c326105c8 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index f8c83ac7998a..2f8cc3ebfa05 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 894230ad1d11..119a20db590d 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 034bcc168b9e..2dd42caa19c2 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 417f8ba3a65f..5f98c468a61c 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: 2022-10-01 +date: 2022-10-02 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 7e46d2dcb1f9..15376c275760 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 5dc82ade1a64..f4f8350b8df6 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index e88bca58e89e..1121ec512bc3 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: 2022-10-01 +date: 2022-10-02 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 dee1f848efd6..fe90ec4cc221 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index ef7abd6df1bf..0de9f54e6299 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index cb067e4a5e48..007e3dd15767 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 2c28a423148f..cafb9d704b49 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: 2022-10-01 +date: 2022-10-02 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index e1380d56c305..ea7bb98fca11 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: 2022-10-01 +date: 2022-10-02 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 960bb4da53ce..cd15a3909e9c 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 00938629a961..f418088dad24 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 0a5ae8a689d8..8aef1a04d1a5 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: 2022-10-01 +date: 2022-10-02 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 b6440aacaa07..a784f125dd4a 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: 2022-10-01 +date: 2022-10-02 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 f92a80367e02..752a04593d5e 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: 2022-10-01 +date: 2022-10-02 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 56e2cd559c03..7c7ace4751a9 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: 2022-10-01 +date: 2022-10-02 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 23bb7dc57c4e..b76a86813447 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: 2022-10-01 +date: 2022-10-02 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 c685665c1cd4..a5ff4e0a5f17 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index e603b0c70740..e0f55f2faed9 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 0aa2a7e6d7e2..9adf212a87d0 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index d327087b2929..77cd942716ab 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 3912476bf911..ac51a3118543 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 2f31f51131f6..a1831303286b 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: 2022-10-01 +date: 2022-10-02 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 4777f33dc85d..5e5dec3f0efa 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: 2022-10-01 +date: 2022-10-02 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 07b9c7684cbb..25b3f6af8b10 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: 2022-10-01 +date: 2022-10-02 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 3c721f46f48c..09850981c921 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: 2022-10-01 +date: 2022-10-02 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 1d452af9ea15..6de7cb1f0d6c 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: 2022-10-01 +date: 2022-10-02 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 59df749d4429..baf98e2fd0b0 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: 2022-10-01 +date: 2022-10-02 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 8063f1fd1592..500c8c72ca83 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: 2022-10-01 +date: 2022-10-02 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 184877c74cfa..5b26d74de8a7 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: 2022-10-01 +date: 2022-10-02 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 933a87c620ba..05e6ed82f6dd 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: 2022-10-01 +date: 2022-10-02 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 0f3f3805ca32..e8c0fc13f6e1 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: 2022-10-01 +date: 2022-10-02 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 6b810acdc8e7..8203f6334e05 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: 2022-10-01 +date: 2022-10-02 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 aceb1ec1d65d..a5fea43827df 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: 2022-10-01 +date: 2022-10-02 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 11cfe00c5eb6..5fc96c25aa1d 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: 2022-10-01 +date: 2022-10-02 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 4e4ba39e56d9..205d44e5872c 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: 2022-10-01 +date: 2022-10-02 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 5a002fe9b634..3d373165a928 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: 2022-10-01 +date: 2022-10-02 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 53bffb726d7e..2d765470b354 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index ba975b844692..760277af99b7 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: 2022-10-01 +date: 2022-10-02 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 9c30c4297261..bbbfb6144deb 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index cf6491d338fe..09dad0109548 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index e055ce5c7dbe..52a088948e2b 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: 2022-10-01 +date: 2022-10-02 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 63958a2cb029..472e6b89741c 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: 2022-10-01 +date: 2022-10-02 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 e8dacbe7203b..3999dd6522fd 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: 2022-10-01 +date: 2022-10-02 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 421f7f9d7187..2c6d531cd186 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: 2022-10-01 +date: 2022-10-02 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 99d63502639c..9ecba55b278c 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: 2022-10-01 +date: 2022-10-02 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 2deff8486497..2c999a65117a 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: 2022-10-01 +date: 2022-10-02 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 883bd3ab03a8..7426d22515d5 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: 2022-10-01 +date: 2022-10-02 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 0db5daddc45c..d815677cdbbb 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: 2022-10-01 +date: 2022-10-02 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 a9353ec77404..285fff4f69d5 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: 2022-10-01 +date: 2022-10-02 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 99d9c415c15b..e753d5c2306e 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: 2022-10-01 +date: 2022-10-02 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 2417b72b9ffe..3e801da7773b 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: 2022-10-01 +date: 2022-10-02 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 68dfc645ddd8..7d0ed072b340 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: 2022-10-01 +date: 2022-10-02 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 8f977f1cc7a9..04e54301c092 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: 2022-10-01 +date: 2022-10-02 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 109a924fb3b9..574f7592eec1 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: 2022-10-01 +date: 2022-10-02 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 07a01ac671da..9886feeb304b 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: 2022-10-01 +date: 2022-10-02 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 10af2f7f2c16..3688d0c50fd5 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: 2022-10-01 +date: 2022-10-02 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 0b0f2e48ec0a..49e38c10a1ab 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: 2022-10-01 +date: 2022-10-02 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 956de3dd0584..baf10caeedb7 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: 2022-10-01 +date: 2022-10-02 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 f10769e16c69..05d0ab8de20a 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: 2022-10-01 +date: 2022-10-02 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 a99179ef9d40..5a6e193562b5 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: 2022-10-01 +date: 2022-10-02 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 6abf6549e056..fd0593f5bf74 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: 2022-10-01 +date: 2022-10-02 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index e44419942c79..6f8f8a20349f 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index e25a62c5079a..d3796d5d79dd 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: 2022-10-01 +date: 2022-10-02 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 28fe6feb5d19..6ba6f268cd13 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 05b56017a08f..de90284104e3 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: 2022-10-01 +date: 2022-10-02 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 be9d33d159f3..13b3aa2b6ebb 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: 2022-10-01 +date: 2022-10-02 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 dd904c049f3b..e57f44e869e1 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index b854992fde33..730dfdd54832 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: 2022-10-01 +date: 2022-10-02 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 82eab77aca0c..3e4061d43938 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index bd59838a870d..80bce2c3d3bb 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: 2022-10-01 +date: 2022-10-02 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 c5b72af903b3..38296f51f0ea 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index abd0555459d9..d038b65a9b43 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 09bc46b76cc9..1dc49f971de9 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 60509b4421a9..cc8f70eddfb4 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 614b52b535a9..fa9aec2765d0 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: 2022-10-01 +date: 2022-10-02 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index 86b99ea131b4..a23a496ec08a 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 1411d1f11e0f..e22681ed1b21 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: 2022-10-01 +date: 2022-10-02 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 124801b6982e..4e184219ce29 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: 2022-10-01 +date: 2022-10-02 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 286cf41a0f81..e2ede8850299 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: 2022-10-01 +date: 2022-10-02 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 1d15139e5ad5..9fc2d665396b 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 3fa079e3178f..7822a652e9dc 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index ac6ccdaefbbf..3f7cc296f078 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: 2022-10-01 +date: 2022-10-02 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 e8016cd77287..721d581c9aeb 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: 2022-10-01 +date: 2022-10-02 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 dff8ce771bde..331c1c9cc68e 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: 2022-10-01 +date: 2022-10-02 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 318aa79a2975..927b730fbd08 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 01c5bc3141bc..29f7f9b863fe 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: 2022-10-01 +date: 2022-10-02 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 e1894af2cbfa..cfe740cacdaf 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: 2022-10-01 +date: 2022-10-02 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 d592aa08d875..1f90c8557e07 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: 2022-10-01 +date: 2022-10-02 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 a65fb069d0ac..27be5086f77e 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index e4e33df28af3..0fe469c82799 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index bcb5fc743472..0dab3bc7dfeb 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: 2022-10-01 +date: 2022-10-02 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 e1409873d045..606529ea480d 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: 2022-10-01 +date: 2022-10-02 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 d6ef5a21773f..cb8141b2fa2e 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f725548e9cf9..a220cc633fdb 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index c72b46a487e9..d57492b57703 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: 2022-10-01 +date: 2022-10-02 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 41785b90b47d..4fa7372e846d 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 4d964c688c8a..ace04be9dfd6 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index d08449754434..ea74ae679ce8 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index e40080c8ecf5..63effa9722c2 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 266faca28aed..0c73ce5aa345 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 65ed265de39c..51cba79e857c 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 581cd5ef4d51..b7aa3b787718 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: 2022-10-01 +date: 2022-10-02 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 817f03f981b1..877b703d7e16 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index e074406c74e8..9f3abb5e1d97 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: 2022-10-01 +date: 2022-10-02 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 5c1dc0c2685a..53a33e3f3e02 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: 2022-10-01 +date: 2022-10-02 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 df1bff571297..4e870d52ebe4 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: 2022-10-01 +date: 2022-10-02 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 ec349c4ad0b5..3a2aa8b507fe 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: 2022-10-01 +date: 2022-10-02 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 5edda54c6f13..4092856f8548 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: 2022-10-01 +date: 2022-10-02 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 d4d313bef79f..6e00cff68eea 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: 2022-10-01 +date: 2022-10-02 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 1ae6118247ce..92585b79379f 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: 2022-10-01 +date: 2022-10-02 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 3a2c36b7cc7f..f97c77aca56d 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: 2022-10-01 +date: 2022-10-02 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 6e788653e193..69df9871d9f6 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: 2022-10-01 +date: 2022-10-02 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 1e81b07e6d24..9e3ca4f8d4e8 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: 2022-10-01 +date: 2022-10-02 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 2146968cda90..ce10df747f58 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: 2022-10-01 +date: 2022-10-02 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 d88e2e95c638..db9cf0112901 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: 2022-10-01 +date: 2022-10-02 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 dd4d7868a899..88a61fbfbefd 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 549878d171ab..574757b6488d 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: 2022-10-01 +date: 2022-10-02 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 eac5df4aa58b..fd2ceda90f9e 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 212526e3c021..dae50a1df7b6 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: 2022-10-01 +date: 2022-10-02 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 a3534fb4ba27..da7c56ca51c5 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 5c626675eef6..12fb3c0f940c 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: 2022-10-01 +date: 2022-10-02 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 b8766114204c..ad1ae785ccc9 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: 2022-10-01 +date: 2022-10-02 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 5bdce3da3e22..366616671516 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: 2022-10-01 +date: 2022-10-02 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 415c51b650dc..811c79896d7e 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: 2022-10-01 +date: 2022-10-02 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 e730f9b56148..0f244bdfa77a 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: 2022-10-01 +date: 2022-10-02 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 8cc947166e67..27b6af9549d7 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: 2022-10-01 +date: 2022-10-02 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 7d0012a2281a..e40ec8f867a6 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: 2022-10-01 +date: 2022-10-02 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 27fb11dfae9f..15225549bbe7 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: 2022-10-01 +date: 2022-10-02 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 e358c37feb52..e962b1c74b73 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: 2022-10-01 +date: 2022-10-02 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 54c0a5d93409..bc8d91d1ee1d 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: 2022-10-01 +date: 2022-10-02 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 9c00897d8c1f..f12a30a01fea 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: 2022-10-01 +date: 2022-10-02 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 510d527d883c..0c044093de8d 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: 2022-10-01 +date: 2022-10-02 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 c94cb7a1a751..d62847550662 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: 2022-10-01 +date: 2022-10-02 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 a67988ed1155..a11e23546d22 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: 2022-10-01 +date: 2022-10-02 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 a2d9c4d869b7..273a9ab86497 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index ccea854bb073..d3614845b248 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 76011654fbea..efac37faf56d 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: 2022-10-01 +date: 2022-10-02 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 14a0d57226b4..b0ffa71265a1 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index f7410808300e..ef65b102aed7 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: 2022-10-01 +date: 2022-10-02 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 a62461b88cee..e41693e5efca 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: 2022-10-01 +date: 2022-10-02 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 7d7a4df32df8..cfafdaf06802 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: 2022-10-01 +date: 2022-10-02 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 3a1a53675dee..f63baebfa4b2 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: 2022-10-01 +date: 2022-10-02 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 cfdaf08f6cc1..b91e25ae3893 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: 2022-10-01 +date: 2022-10-02 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 b26a487e9cbb..49ba54cdc57d 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: 2022-10-01 +date: 2022-10-02 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 a3aaeb805b0c..93e84b16f4d2 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: 2022-10-01 +date: 2022-10-02 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 aff984617861..90bae1cb686f 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: 2022-10-01 +date: 2022-10-02 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 c1f44c36c05d..06b9a57efddf 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: 2022-10-01 +date: 2022-10-02 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 179e2273cf67..7aafcad29e0f 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: 2022-10-01 +date: 2022-10-02 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 691934f5c9b1..324013c67331 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: 2022-10-01 +date: 2022-10-02 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 a18309726008..d8e1da6a13e9 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: 2022-10-01 +date: 2022-10-02 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 9733a2e51f76..4722a4b75256 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 1b87bc4d3156..36d12fab03cf 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: 2022-10-01 +date: 2022-10-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From de1c22a07429001ce582dd28d97860754f5b5220 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Sun, 2 Oct 2022 22:39:26 -0600 Subject: [PATCH 180/185] [api-docs] Daily api_docs build (#142438) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_common.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- api_docs/kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- api_docs/kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- api_docs/kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- api_docs/kbn_core_application_browser_internal.mdx | 2 +- api_docs/kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- api_docs/kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- api_docs/kbn_core_deprecations_browser_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- api_docs/kbn_core_deprecations_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_mocks.mdx | 2 +- api_docs/kbn_core_environment_server_internal.mdx | 2 +- api_docs/kbn_core_environment_server_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_browser.mdx | 2 +- api_docs/kbn_core_execution_context_browser_internal.mdx | 2 +- api_docs/kbn_core_execution_context_browser_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_common.mdx | 2 +- api_docs/kbn_core_execution_context_server.mdx | 2 +- api_docs/kbn_core_execution_context_server_internal.mdx | 2 +- api_docs/kbn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- api_docs/kbn_core_http_context_server_mocks.mdx | 2 +- api_docs/kbn_core_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_router_server_internal.mdx | 2 +- api_docs/kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- api_docs/kbn_core_injected_metadata_browser.mdx | 2 +- api_docs/kbn_core_injected_metadata_browser_mocks.mdx | 2 +- api_docs/kbn_core_integrations_browser_internal.mdx | 2 +- api_docs/kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- api_docs/kbn_core_notifications_browser_internal.mdx | 2 +- api_docs/kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- api_docs/kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_api_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- .../kbn_core_saved_objects_import_export_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- api_docs/kbn_core_saved_objects_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- api_docs/kbn_core_test_helpers_deprecations_getters.mdx | 2 +- api_docs/kbn_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- api_docs/kbn_core_ui_settings_server_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- api_docs/kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_get_repo_files.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- api_docs/kbn_securitysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_alerting_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- api_docs/kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_user_profile_components.mdx | 2 +- api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 404 files changed, 404 insertions(+), 404 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index f91e6ca14c5c..8c70634191ac 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: 2022-10-02 +date: 2022-10-03 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 7c38365d9afe..5f708b7bd5b2 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 1adc1287e6f2..affc8c08a679 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: 2022-10-02 +date: 2022-10-03 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 3da854ba53f1..dba2b5b8a03f 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: 2022-10-02 +date: 2022-10-03 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 5831360d8a8c..e327d3d9ef26 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index a25e8c846c7e..8465fcc62bbb 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: 2022-10-02 +date: 2022-10-03 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 06dd79899f4f..b4d39167b8af 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: 2022-10-02 +date: 2022-10-03 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 852476836414..964d7c080f50 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: 2022-10-02 +date: 2022-10-03 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 aed43e616a03..1b56bb9f2bb5 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: 2022-10-02 +date: 2022-10-03 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 166c25203c17..5971c6140b9d 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: 2022-10-02 +date: 2022-10-03 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 4cf122ec44f1..07a722e9fc63 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index afce96b1debd..8cb19bfe8beb 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 6da76742d8c2..6c3113198141 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: 2022-10-02 +date: 2022-10-03 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 c5b317f06f24..69717a2855de 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 7af97619b1a3..d120725ebf6c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 5f59293c8f37..2ce73b73214a 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 6778fc3543db..c9bd9c16e721 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: 2022-10-02 +date: 2022-10-03 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 711da58211c5..7fa93934def0 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: 2022-10-02 +date: 2022-10-03 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 a4aac32db170..4f0b134f643c 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: 2022-10-02 +date: 2022-10-03 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 74a573292d79..3cda1cdf63de 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 52ec376fb7cb..09c0ef0d696b 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: 2022-10-02 +date: 2022-10-03 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 c0cda63f6162..c3b582462fd7 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 3a448dea572e..34855c7100ce 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: 2022-10-02 +date: 2022-10-03 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 c4933b68c936..5daa20fdddfe 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: 2022-10-02 +date: 2022-10-03 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 35b2849b1521..fa43c84f3166 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: 2022-10-02 +date: 2022-10-03 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 8b66e010c36d..817619ccf49c 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: 2022-10-02 +date: 2022-10-03 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 17ed535ee87c..6df0430d04c2 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index f4b1fa55c1ed..3c34e78a89e7 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 0c5bbd60e517..abe2810379ec 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 60d74fa28721..882abd4cb615 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 06dfec816d60..76be86527aae 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: 2022-10-02 +date: 2022-10-03 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 e703c38627cb..9c9793bd2bcf 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: 2022-10-02 +date: 2022-10-03 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 591663448e85..bc90ebb67c09 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 9782e32b7985..4c4b222a02a7 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: 2022-10-02 +date: 2022-10-03 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 84dd77d22a23..5ab087491141 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: 2022-10-02 +date: 2022-10-03 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 ffd479100976..5aa1eae40de5 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: 2022-10-02 +date: 2022-10-03 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 6d8e9e20d699..ba021086e294 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 661382fdf519..bf991156be21 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index d752e0f31cc9..2c09bd5d65d4 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index cfa039d75aa8..482e1d361a15 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 1ea659904646..2ab9af9772d0 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: 2022-10-02 +date: 2022-10-03 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 2e6be367a1d9..908d90469c75 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: 2022-10-02 +date: 2022-10-03 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 f4086885e507..aa2ac31f046e 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: 2022-10-02 +date: 2022-10-03 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 b0d7b0b0e664..fd4412ad3f9e 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: 2022-10-02 +date: 2022-10-03 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 f58979b777a3..4f1bd9723f36 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: 2022-10-02 +date: 2022-10-03 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 1b1035d10c9e..d17081eed3cb 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: 2022-10-02 +date: 2022-10-03 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 8c3a8f9b3933..daa61ecf37e7 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: 2022-10-02 +date: 2022-10-03 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 646de5a02517..aaf66fd40fa4 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: 2022-10-02 +date: 2022-10-03 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 4913f9da7c39..8e5467dc9cff 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: 2022-10-02 +date: 2022-10-03 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 906b0f9a865c..a2a032c0c0bd 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: 2022-10-02 +date: 2022-10-03 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 32e6eca8a079..e650bf57eac7 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: 2022-10-02 +date: 2022-10-03 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 7eb965182927..cf00311b239a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index c727f32752b6..0eed4b0b713d 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 63b7d486fa20..353dfa02a783 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: 2022-10-02 +date: 2022-10-03 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 14e361da2246..8b7f40f4a359 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: 2022-10-02 +date: 2022-10-03 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 4c8ac407c2a2..040d82060400 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index a5923aea7ee0..768f6188371f 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: 2022-10-02 +date: 2022-10-03 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 d40d7e303d6b..8491d90df244 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index bafd3ddd8518..b63495e17792 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 42378d31281f..97d12a56906f 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: 2022-10-02 +date: 2022-10-03 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 d183f51fa149..899b3205a819 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: 2022-10-02 +date: 2022-10-03 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 388cdf51fff4..30beb41d12fa 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 36f4a84d6d37..e3ee1798496d 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: 2022-10-02 +date: 2022-10-03 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 f142e5206790..34f9d8fcde85 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index ba44a8375901..7ab9b9d8573c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index cebd40add541..4cb29a4632ca 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index bcfc55b7a310..6f257bb85432 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index de04343d39dd..0dcf9a1b797b 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 640d8bc0dadd..60ab8f506c3a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 37748e5923d7..c459ac8369da 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 4f93c597b2e4..aaf236aab90f 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 653e93710bb2..35df4e16f17c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index ab8ade876b3b..0d18980e0b1c 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 94415cdc496c..c4b9427b1a33 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index bc6057bd6596..f06cb724cc72 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 953ad7736eaa..cd5176593f6a 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 68b4f9a89cc3..d6317c64bf48 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 633a769efa1a..e916c19f1619 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: 2022-10-02 +date: 2022-10-03 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_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 99ce7b8a19bb..9ba0d5aeeacf 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 962b5aa586a1..ca208a21625b 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 50f8cd64aee1..7a1ad1863e07 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 476bfef859d0..6e102733b051 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: 2022-10-02 +date: 2022-10-03 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 549084cde27d..cfd53f8e091f 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: 2022-10-02 +date: 2022-10-03 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 2256eb2966ed..c28b07464ba4 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: 2022-10-02 +date: 2022-10-03 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 ad42b9ac3ee4..d5c55b31b68c 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: 2022-10-02 +date: 2022-10-03 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 22a6845fb05d..c2de5353062a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 2d54eb64e603..c5026a994bbf 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: 2022-10-02 +date: 2022-10-03 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 a52202f78637..a81d5f720c20 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: 2022-10-02 +date: 2022-10-03 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 192f0654b7d5..3ca7cf87f881 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: 2022-10-02 +date: 2022-10-03 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 5633664daf6d..f4fd135eead5 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index bbf2c310a588..2cb822b64537 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index e090ff0a2674..2cf8df7f6fb1 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: 2022-10-02 +date: 2022-10-03 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 fe43394f77fb..217056d756e6 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: 2022-10-02 +date: 2022-10-03 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 e9c9b866f0fe..8674be0b88a2 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: 2022-10-02 +date: 2022-10-03 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 a86d4bce5628..a3e9193110cf 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: 2022-10-02 +date: 2022-10-03 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 9b2fe9f9f192..87edbd5da157 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: 2022-10-02 +date: 2022-10-03 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 c2dc15b657d7..eba99f774098 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: 2022-10-02 +date: 2022-10-03 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 e935da728930..6cd2f00a3f32 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: 2022-10-02 +date: 2022-10-03 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 2a05ae0c01c5..0e0674185951 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: 2022-10-02 +date: 2022-10-03 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 2e74725d2f3d..f58bebcf2f5d 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: 2022-10-02 +date: 2022-10-03 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 4a541f590e2f..3d4d7035f6c1 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: 2022-10-02 +date: 2022-10-03 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 3463e8e9384e..7681b7a4a388 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: 2022-10-02 +date: 2022-10-03 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 834faf66c67a..02a409c5881d 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: 2022-10-02 +date: 2022-10-03 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index f22445d1e10e..b60d8471e15d 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: 2022-10-02 +date: 2022-10-03 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 fe429f0f502a..39463461d916 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: 2022-10-02 +date: 2022-10-03 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 9d162ecc81bb..2a64da20012e 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: 2022-10-02 +date: 2022-10-03 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 be46292c9bc9..b2056befb2ac 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: 2022-10-02 +date: 2022-10-03 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 aa7f5b3d5df7..6d28c5498417 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: 2022-10-02 +date: 2022-10-03 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 1380bd04beea..c7628a5a59c6 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: 2022-10-02 +date: 2022-10-03 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 00087d6131cc..e01e96164ef4 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: 2022-10-02 +date: 2022-10-03 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 e4e5a9223ba0..9ef6e61852ed 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_chrome_browser.mdx index 251b2d507b16..57e8483cb0f7 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: 2022-10-02 +date: 2022-10-03 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 10c16f282a6f..c4fabecffea3 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: 2022-10-02 +date: 2022-10-03 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 8d29eb118d55..1d7858167c97 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: 2022-10-02 +date: 2022-10-03 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 0d64e581664e..3914aa766a64 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: 2022-10-02 +date: 2022-10-03 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 6d31a54a5605..fca144e245a7 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: 2022-10-02 +date: 2022-10-03 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 9c7aa1019372..5be603ffa1f9 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_deprecations_common.mdx index bc77cf44b3bd..afde35a745d3 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index a976ebcc9a0e..92ba606e6044 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: 2022-10-02 +date: 2022-10-03 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 a3af9d6298c5..9b3c1070f662 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: 2022-10-02 +date: 2022-10-03 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 42feea3f29fb..e871d2dc0a94 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: 2022-10-02 +date: 2022-10-03 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 1d24379f441f..d591288b8131 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: 2022-10-02 +date: 2022-10-03 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 48c6d6862f4d..7cd809b6b97e 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: 2022-10-02 +date: 2022-10-03 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 8195d4f62389..273051738c41 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: 2022-10-02 +date: 2022-10-03 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 85fa2c471574..0cddffa5aeab 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: 2022-10-02 +date: 2022-10-03 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 feabe3a92d7f..88eb042d9820 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: 2022-10-02 +date: 2022-10-03 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 7b9875f589c6..bbf4d1f0a58e 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: 2022-10-02 +date: 2022-10-03 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 1e0c6217b952..c9f8fdbd9b45 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: 2022-10-02 +date: 2022-10-03 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 7bc107af9a80..f4032d04f97c 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: 2022-10-02 +date: 2022-10-03 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 95497dfe20dc..835c21903b6b 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: 2022-10-02 +date: 2022-10-03 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 5f7c6dc637c8..47f64412fe99 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: 2022-10-02 +date: 2022-10-03 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 bbda34217fa7..082f5dd46ece 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: 2022-10-02 +date: 2022-10-03 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 fad63abe1993..492f874f833e 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: 2022-10-02 +date: 2022-10-03 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 006d8b447663..f8426b783c45 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: 2022-10-02 +date: 2022-10-03 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 96e88c7e5c2a..6efeffd1c91c 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: 2022-10-02 +date: 2022-10-03 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 7e3559438efa..ed8e5ae438b3 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: 2022-10-02 +date: 2022-10-03 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 e6ab7c3d8915..6441c8e32639 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: 2022-10-02 +date: 2022-10-03 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 17fee1daced5..4f0d3211fa39 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: 2022-10-02 +date: 2022-10-03 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 ed465631b22b..02fa99d7cb6c 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: 2022-10-02 +date: 2022-10-03 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 89eb21cdb4d5..40263469ee78 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: 2022-10-02 +date: 2022-10-03 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 3e1fbaec76d3..a6af55aa79cb 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: 2022-10-02 +date: 2022-10-03 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_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 13817b60a15a..a1c258a426f3 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: 2022-10-02 +date: 2022-10-03 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 3380e7e1a2b4..a937dccab7fb 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: 2022-10-02 +date: 2022-10-03 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 4020a1edd3ae..a6274dd34b37 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: 2022-10-02 +date: 2022-10-03 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 a48229db2238..6293ae9b58af 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: 2022-10-02 +date: 2022-10-03 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 6c86cd7218b8..f11227d039fc 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: 2022-10-02 +date: 2022-10-03 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 a41bd0793732..d0ea0e82f768 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: 2022-10-02 +date: 2022-10-03 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 98354a9107f3..7654b3f70cc8 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 99ac50e292b6..8a2d3a3de333 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_http_server.mdx index fd022dfb3eea..62b834449c23 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index f058d1e28dec..bbda88d5c238 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 1b6cf98922c1..e99fba0127cc 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: 2022-10-02 +date: 2022-10-03 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 bc10c115fb75..c6da1dc82bc9 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: 2022-10-02 +date: 2022-10-03 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 02d199810fef..140e34612800 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: 2022-10-02 +date: 2022-10-03 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 b391482c1e67..ddd02e61d7ee 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: 2022-10-02 +date: 2022-10-03 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 8bdf305b1964..56636905f7af 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: 2022-10-02 +date: 2022-10-03 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 949a0fde56bc..f8ed8c6f0bec 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index f93ad919d9fe..9a02125114dc 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 6389c0fa21a8..d6a078aef0df 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: 2022-10-02 +date: 2022-10-03 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 f1c6bacc8a19..38c283eaaf97 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: 2022-10-02 +date: 2022-10-03 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 91fca25dd497..5cc58ad5a914 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: 2022-10-02 +date: 2022-10-03 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 8aee8a80734c..1ab8822af225 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: 2022-10-02 +date: 2022-10-03 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 d76de30044e4..99d9d3f32d3e 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: 2022-10-02 +date: 2022-10-03 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 575ad511b8fd..5065fff3211f 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: 2022-10-02 +date: 2022-10-03 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 02793dca4902..2e6959cc8759 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: 2022-10-02 +date: 2022-10-03 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 259d3fd65a26..81a53652b4ea 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: 2022-10-02 +date: 2022-10-03 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 81710e21c33f..3d5b6fffdd51 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: 2022-10-02 +date: 2022-10-03 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 e3e171db8439..14756f136bd3 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: 2022-10-02 +date: 2022-10-03 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 bacd5044596d..087dd2dd357c 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: 2022-10-02 +date: 2022-10-03 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 eadb43389a16..5f5ea85f8005 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: 2022-10-02 +date: 2022-10-03 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 baaf23f8822d..6216a6d589ce 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: 2022-10-02 +date: 2022-10-03 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 0b68bd525a64..82c2c06e0845 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: 2022-10-02 +date: 2022-10-03 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 08f701606253..b4eeaf823c52 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: 2022-10-02 +date: 2022-10-03 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 5b065122b113..04c3664f49b6 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: 2022-10-02 +date: 2022-10-03 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 e540991a1252..edcf4af2b62a 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: 2022-10-02 +date: 2022-10-03 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 8bea394dae01..d120b6ccece7 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: 2022-10-02 +date: 2022-10-03 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 ad8e17b446e9..e4152c45f1f6 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: 2022-10-02 +date: 2022-10-03 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 514474ea2ef2..a571fb85c2ef 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: 2022-10-02 +date: 2022-10-03 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 bae3dbcf6325..8f4457512da8 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: 2022-10-02 +date: 2022-10-03 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 054489949922..6e25c69de32e 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: 2022-10-02 +date: 2022-10-03 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 9b739d1209d1..ce41f3aac1f4 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: 2022-10-02 +date: 2022-10-03 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 5b9a435bacaf..0b413f962b27 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: 2022-10-02 +date: 2022-10-03 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 b9fa84b19f8c..b31af30c1603 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: 2022-10-02 +date: 2022-10-03 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 0906ee4ed838..ec6b64842af4 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: 2022-10-02 +date: 2022-10-03 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 6b97c1f88067..583b5bc973ad 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: 2022-10-02 +date: 2022-10-03 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 520e18ebb3e7..383e9c6511ef 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: 2022-10-02 +date: 2022-10-03 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 020fabbec183..e07f5797c7f7 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: 2022-10-02 +date: 2022-10-03 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 6da27ef12143..8a3662b8b470 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: 2022-10-02 +date: 2022-10-03 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_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index df6fc60584f4..7244cfe8beb6 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.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 81a29c9d9a72..9a62ec00bb37 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: 2022-10-02 +date: 2022-10-03 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 b2eb85135b8d..4068fcf5e73b 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: 2022-10-02 +date: 2022-10-03 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 d6eb21156cef..7af80f752794 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: 2022-10-02 +date: 2022-10-03 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 c0db5893ecdc..83b6f0888728 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: 2022-10-02 +date: 2022-10-03 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 6b1df9e60f5a..3d8e3d03be29 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: 2022-10-02 +date: 2022-10-03 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 3767a975778f..ad03d6488f2a 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: 2022-10-02 +date: 2022-10-03 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 46e635afccff..b06d26ed9bea 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: 2022-10-02 +date: 2022-10-03 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 d9dcf279b04e..6532dfcaeaf8 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: 2022-10-02 +date: 2022-10-03 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 b273522ddd59..53092b90fc15 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: 2022-10-02 +date: 2022-10-03 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 4a63a5bd89ef..3d1544f4f110 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: 2022-10-02 +date: 2022-10-03 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 c02fc259a89b..c662be1f8fb0 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: 2022-10-02 +date: 2022-10-03 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 78b702fb4478..8a27161ad18d 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: 2022-10-02 +date: 2022-10-03 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 90c8b849e0ba..c73c73d97418 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: 2022-10-02 +date: 2022-10-03 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 f349189ab730..e8fe3856fa57 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: 2022-10-02 +date: 2022-10-03 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 8c5aeb1e8262..5eb6ed5fabf2 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: 2022-10-02 +date: 2022-10-03 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_status_common.mdx b/api_docs/kbn_core_status_common.mdx index d9566708b3ae..3f9ec5be08ad 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: 2022-10-02 +date: 2022-10-03 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 a55470733390..fd7956ee7ab4 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: 2022-10-02 +date: 2022-10-03 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 439d25504386..71ceb6a7bede 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: 2022-10-02 +date: 2022-10-03 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 96f568f178a4..3916cb3c3119 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: 2022-10-02 +date: 2022-10-03 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 85b8475863cf..966876de9b0c 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: 2022-10-02 +date: 2022-10-03 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 957576c2e148..f3ad0c01cc37 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: 2022-10-02 +date: 2022-10-03 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 aeaf0b8ee110..995497dffa50 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: 2022-10-02 +date: 2022-10-03 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 45aa25f3d172..2db8ecd68b0d 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: 2022-10-02 +date: 2022-10-03 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_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 4b3a42ce6cc7..38778f82b4a7 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 92f080e0f528..d561c846ba52 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: 2022-10-02 +date: 2022-10-03 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 4a1529581735..f4b08646423d 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: 2022-10-02 +date: 2022-10-03 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 6fb6935573a7..607eaa47b682 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: 2022-10-02 +date: 2022-10-03 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 31697c023dda..15ecdc9fee50 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: 2022-10-02 +date: 2022-10-03 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 574d0ec8e7b4..6644389a6513 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: 2022-10-02 +date: 2022-10-03 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 0997bcdc2211..d486425f2067 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: 2022-10-02 +date: 2022-10-03 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 7b4df9498b90..98b9aa8e6584 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: 2022-10-02 +date: 2022-10-03 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 d222af41c3fd..c287c7d3717f 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_usage_data_server.mdx index 976fc475761a..8d5b6922438e 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 9bd4dab752ef..e433b1846305 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: 2022-10-02 +date: 2022-10-03 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.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 8dd7253ab6ab..ad3dac588eb6 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: 2022-10-02 +date: 2022-10-03 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_crypto.mdx b/api_docs/kbn_crypto.mdx index a8049f93e042..0e4143134287 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: 2022-10-02 +date: 2022-10-03 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 d5209c039a16..7f67262cf91c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 76052024f029..86cc145ec159 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 39e11f8614d8..e46730cf719c 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: 2022-10-02 +date: 2022-10-03 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 95871faf8678..54d0f8713d0f 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: 2022-10-02 +date: 2022-10-03 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 198147e7c822..b646fc86f43f 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: 2022-10-02 +date: 2022-10-03 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 b9922d230eb7..83091d9879dc 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 3fe12c91c32a..8913a1a03ab8 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: 2022-10-02 +date: 2022-10-03 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 3545dbecd696..a5d4079c284b 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index b4270c9011bb..8370c22a01f1 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index ca423dde4451..4ad77bfff655 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: 2022-10-02 +date: 2022-10-03 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 f422e71b9a8b..bf9c27ac92b2 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: 2022-10-02 +date: 2022-10-03 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 a0bf853e54fd..632303fa82c9 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: 2022-10-02 +date: 2022-10-03 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 42124a156bff..6a6ac63f69ac 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: 2022-10-02 +date: 2022-10-03 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 883203cc6f10..e0525000b5f9 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 8791798c6465..f9ad3846f19f 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index ae1139f5d360..9bc0b9216824 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: 2022-10-02 +date: 2022-10-03 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_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 1c2551073eb2..359334478d03 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: 2022-10-02 +date: 2022-10-03 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_generate.mdx b/api_docs/kbn_generate.mdx index 9e670335e3a9..151e0da4dd3b 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 816ea84cc969..161354bbbcb1 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 88db861974d6..1e054c0706c6 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: 2022-10-02 +date: 2022-10-03 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 be13032975df..0852dda61f29 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 71c8b52c08c7..d4cca0681860 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: 2022-10-02 +date: 2022-10-03 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 67b69bdc6937..54d4d15fc741 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: 2022-10-02 +date: 2022-10-03 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 287c326105c8..c7ff5b8ab505 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 2f8cc3ebfa05..262e79df335d 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 119a20db590d..df20b5d894c5 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 2dd42caa19c2..4994d60766a4 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 5f98c468a61c..e5e069f91c95 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: 2022-10-02 +date: 2022-10-03 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 15376c275760..21477f1c435c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index f4f8350b8df6..6d9b4a9bb317 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 1121ec512bc3..712dd79f5535 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: 2022-10-02 +date: 2022-10-03 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 fe90ec4cc221..6dcc57446aaa 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 0de9f54e6299..788e96c22f5c 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 007e3dd15767..8e2ce8b3d9fd 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index cafb9d704b49..25b2b17e3997 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: 2022-10-02 +date: 2022-10-03 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index ea7bb98fca11..5f24dc1806b0 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: 2022-10-02 +date: 2022-10-03 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index cd15a3909e9c..2fa9ac8d84b7 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index f418088dad24..c5b48fbeff00 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 8aef1a04d1a5..2a5d7bc10afb 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: 2022-10-02 +date: 2022-10-03 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 a784f125dd4a..3f87a2b8b79d 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: 2022-10-02 +date: 2022-10-03 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 752a04593d5e..76d9089526ce 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: 2022-10-02 +date: 2022-10-03 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_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 7c7ace4751a9..3e83e64f435d 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: 2022-10-02 +date: 2022-10-03 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_generator.mdx b/api_docs/kbn_plugin_generator.mdx index b76a86813447..dc887c9ce305 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: 2022-10-02 +date: 2022-10-03 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 a5ff4e0a5f17..30cf3560edd3 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index e0f55f2faed9..ae485f79249a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 9adf212a87d0..8249b1eae5a3 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 77cd942716ab..a4ef149f8033 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index ac51a3118543..ea7380185e08 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index a1831303286b..4f2b01b57863 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: 2022-10-02 +date: 2022-10-03 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 5e5dec3f0efa..35651464e503 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: 2022-10-02 +date: 2022-10-03 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 25b3f6af8b10..74a60ea3a6e0 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: 2022-10-02 +date: 2022-10-03 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 09850981c921..a2e5cb79e7ec 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: 2022-10-02 +date: 2022-10-03 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 6de7cb1f0d6c..878d21884f5a 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: 2022-10-02 +date: 2022-10-03 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 baf98e2fd0b0..7009b4610d37 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: 2022-10-02 +date: 2022-10-03 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 500c8c72ca83..1a8635741da3 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: 2022-10-02 +date: 2022-10-03 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 5b26d74de8a7..ba0f3639d8e0 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: 2022-10-02 +date: 2022-10-03 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 05e6ed82f6dd..cc6cd2e0e30a 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: 2022-10-02 +date: 2022-10-03 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 e8c0fc13f6e1..c160feacf522 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: 2022-10-02 +date: 2022-10-03 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 8203f6334e05..1a0fb17e5ced 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: 2022-10-02 +date: 2022-10-03 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 a5fea43827df..7aa3b1237d1b 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: 2022-10-02 +date: 2022-10-03 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 5fc96c25aa1d..7b831d821337 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: 2022-10-02 +date: 2022-10-03 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 205d44e5872c..e82e1785f249 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: 2022-10-02 +date: 2022-10-03 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 3d373165a928..60f6a7802671 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: 2022-10-02 +date: 2022-10-03 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 2d765470b354..d5b974e9060a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 760277af99b7..434f6ebf7360 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: 2022-10-02 +date: 2022-10-03 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_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index bbbfb6144deb..aca5844ce1aa 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index 09dad0109548..64ff483b257b 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 52a088948e2b..98810e33e0a4 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: 2022-10-02 +date: 2022-10-03 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 472e6b89741c..f7ca5b161911 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: 2022-10-02 +date: 2022-10-03 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 3999dd6522fd..bae9391ff07c 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: 2022-10-02 +date: 2022-10-03 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 2c6d531cd186..aeabec4b1421 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: 2022-10-02 +date: 2022-10-03 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 9ecba55b278c..7dc8d4c97313 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: 2022-10-02 +date: 2022-10-03 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 2c999a65117a..5386d337930e 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: 2022-10-02 +date: 2022-10-03 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 7426d22515d5..77248704888b 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: 2022-10-02 +date: 2022-10-03 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 d815677cdbbb..100b84a823c9 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: 2022-10-02 +date: 2022-10-03 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 285fff4f69d5..fc9392a85f8e 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: 2022-10-02 +date: 2022-10-03 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 e753d5c2306e..75c7b818d2b9 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: 2022-10-02 +date: 2022-10-03 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 3e801da7773b..46c1575ac211 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: 2022-10-02 +date: 2022-10-03 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 7d0ed072b340..ab3b48e84863 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: 2022-10-02 +date: 2022-10-03 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 04e54301c092..be35872625c4 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: 2022-10-02 +date: 2022-10-03 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 574f7592eec1..b5afba9ff421 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: 2022-10-02 +date: 2022-10-03 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 9886feeb304b..da1478041d72 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: 2022-10-02 +date: 2022-10-03 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 3688d0c50fd5..1b1327a86bd0 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: 2022-10-02 +date: 2022-10-03 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 49e38c10a1ab..0e7e8889a58c 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: 2022-10-02 +date: 2022-10-03 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_router.mdx b/api_docs/kbn_shared_ux_router.mdx index baf10caeedb7..f9c07c50257c 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: 2022-10-02 +date: 2022-10-03 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 05d0ab8de20a..a0aca2fcd5aa 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: 2022-10-02 +date: 2022-10-03 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 5a6e193562b5..aeae1c356a86 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: 2022-10-02 +date: 2022-10-03 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 fd0593f5bf74..d5712323de82 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: 2022-10-02 +date: 2022-10-03 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_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 6f8f8a20349f..346607202705 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index d3796d5d79dd..d6c26edac107 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: 2022-10-02 +date: 2022-10-03 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_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 6ba6f268cd13..13db8c8155fd 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index de90284104e3..e96bc3e61652 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: 2022-10-02 +date: 2022-10-03 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 13b3aa2b6ebb..b4aac204fc2f 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: 2022-10-02 +date: 2022-10-03 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 e57f44e869e1..13b0046b3c5f 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 730dfdd54832..5dc57b83e3ca 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: 2022-10-02 +date: 2022-10-03 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 3e4061d43938..924a8f1d2d36 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 80bce2c3d3bb..cc4a486b6dc8 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: 2022-10-02 +date: 2022-10-03 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 38296f51f0ea..38fd9ee19295 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index d038b65a9b43..efc952ecde5a 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 1dc49f971de9..1c63f3fb9930 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index cc8f70eddfb4..346fc07253bf 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index fa9aec2765d0..cc16ed53a0ad 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: 2022-10-02 +date: 2022-10-03 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index a23a496ec08a..768d7656e3a8 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index e22681ed1b21..321db07786d4 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: 2022-10-02 +date: 2022-10-03 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 4e184219ce29..59733af69605 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: 2022-10-02 +date: 2022-10-03 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 e2ede8850299..d34f5eb451f6 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: 2022-10-02 +date: 2022-10-03 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 9fc2d665396b..3301abb7449f 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 7822a652e9dc..7aba97a043a2 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 3f7cc296f078..99437dfa2d79 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: 2022-10-02 +date: 2022-10-03 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 721d581c9aeb..b563f5461da0 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: 2022-10-02 +date: 2022-10-03 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 331c1c9cc68e..5bb528b47021 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: 2022-10-02 +date: 2022-10-03 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 927b730fbd08..b08fc593fdcc 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 29f7f9b863fe..ee26d7bf7b87 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: 2022-10-02 +date: 2022-10-03 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 cfe740cacdaf..f3039f89e8db 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: 2022-10-02 +date: 2022-10-03 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 1f90c8557e07..a2bbb989390e 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: 2022-10-02 +date: 2022-10-03 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 27be5086f77e..a1ea39b243d1 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 0fe469c82799..167f95ca81b4 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 0dab3bc7dfeb..7b13b081164f 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: 2022-10-02 +date: 2022-10-03 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 606529ea480d..84567432aed6 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: 2022-10-02 +date: 2022-10-03 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 cb8141b2fa2e..7e0c0528fef4 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index a220cc633fdb..a7a7a0517b9b 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index d57492b57703..e884c213dddb 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: 2022-10-02 +date: 2022-10-03 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 4fa7372e846d..37856b288370 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index ace04be9dfd6..283d52b0a4c3 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index ea74ae679ce8..278b15e23d47 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 63effa9722c2..2f29ba44a8c0 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 0c73ce5aa345..5b05f78a15a0 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 51cba79e857c..b6b1d856686d 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index b7aa3b787718..f9b349676713 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: 2022-10-02 +date: 2022-10-03 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 877b703d7e16..489f7c32b600 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 9f3abb5e1d97..6a8252bfebe7 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: 2022-10-02 +date: 2022-10-03 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 53a33e3f3e02..fbdfd3cb5bd0 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: 2022-10-02 +date: 2022-10-03 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 4e870d52ebe4..11313a6dc5f2 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: 2022-10-02 +date: 2022-10-03 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 3a2aa8b507fe..3ac4843ea417 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: 2022-10-02 +date: 2022-10-03 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 4092856f8548..17f5b0c7f230 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: 2022-10-02 +date: 2022-10-03 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 6e00cff68eea..5063707c3abd 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: 2022-10-02 +date: 2022-10-03 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 92585b79379f..53186cbce436 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: 2022-10-02 +date: 2022-10-03 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 f97c77aca56d..0032f71ba648 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: 2022-10-02 +date: 2022-10-03 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 69df9871d9f6..b3c954d358cb 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: 2022-10-02 +date: 2022-10-03 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 9e3ca4f8d4e8..280c60872da7 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: 2022-10-02 +date: 2022-10-03 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 ce10df747f58..eac6e8187a83 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: 2022-10-02 +date: 2022-10-03 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 db9cf0112901..50deed0a47fd 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: 2022-10-02 +date: 2022-10-03 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 88a61fbfbefd..64b2f1e872dd 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 574757b6488d..b62b787d9d0e 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: 2022-10-02 +date: 2022-10-03 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 fd2ceda90f9e..088b9d7a12e2 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index dae50a1df7b6..eaddd45f9b77 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: 2022-10-02 +date: 2022-10-03 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 da7c56ca51c5..c6ba8145d4c6 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 12fb3c0f940c..daaaceba668b 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: 2022-10-02 +date: 2022-10-03 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 ad1ae785ccc9..1196b40d5c3b 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: 2022-10-02 +date: 2022-10-03 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 366616671516..fdd5dfbc025b 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: 2022-10-02 +date: 2022-10-03 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 811c79896d7e..89056c9ebd08 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: 2022-10-02 +date: 2022-10-03 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 0f244bdfa77a..1b7e13dd4d65 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: 2022-10-02 +date: 2022-10-03 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 27b6af9549d7..8e865875b83f 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: 2022-10-02 +date: 2022-10-03 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 e40ec8f867a6..e5cbc94c567a 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: 2022-10-02 +date: 2022-10-03 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 15225549bbe7..7b775d1ce3bc 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: 2022-10-02 +date: 2022-10-03 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 e962b1c74b73..7312616ea8a6 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: 2022-10-02 +date: 2022-10-03 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 bc8d91d1ee1d..4abe6fedf3bf 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: 2022-10-02 +date: 2022-10-03 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 f12a30a01fea..692501acd7de 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: 2022-10-02 +date: 2022-10-03 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 0c044093de8d..9603096cd2b9 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: 2022-10-02 +date: 2022-10-03 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 d62847550662..a9c60110ba13 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: 2022-10-02 +date: 2022-10-03 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 a11e23546d22..734e896cbdf2 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: 2022-10-02 +date: 2022-10-03 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 273a9ab86497..5ac50aa29c08 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index d3614845b248..2818641379fb 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index efac37faf56d..ed31e74c5d7e 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: 2022-10-02 +date: 2022-10-03 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 b0ffa71265a1..e75d86bbab21 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index ef65b102aed7..0ac5762c42af 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: 2022-10-02 +date: 2022-10-03 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 e41693e5efca..b493774dc1eb 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: 2022-10-02 +date: 2022-10-03 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 cfafdaf06802..f3c60dc8b7b4 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: 2022-10-02 +date: 2022-10-03 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 f63baebfa4b2..ec4208a4a06d 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: 2022-10-02 +date: 2022-10-03 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 b91e25ae3893..43f73daa0807 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: 2022-10-02 +date: 2022-10-03 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 49ba54cdc57d..d537797699ca 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: 2022-10-02 +date: 2022-10-03 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 93e84b16f4d2..08324c72b813 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: 2022-10-02 +date: 2022-10-03 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 90bae1cb686f..7d052e7dd40f 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: 2022-10-02 +date: 2022-10-03 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 06b9a57efddf..696edbd5d321 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: 2022-10-02 +date: 2022-10-03 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 7aafcad29e0f..ef87c344661e 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: 2022-10-02 +date: 2022-10-03 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 324013c67331..1a5a44160bf4 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: 2022-10-02 +date: 2022-10-03 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 d8e1da6a13e9..8651bb82ad81 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: 2022-10-02 +date: 2022-10-03 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 4722a4b75256..440839b51793 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 36d12fab03cf..2edc6b736c5b 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: 2022-10-02 +date: 2022-10-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From 8a298e4c8d1a9c75ce081e5c4c91174818e057d9 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 3 Oct 2022 09:52:17 +0200 Subject: [PATCH 181/185] Migrate server-side `rendering` service to packages (#141902) * create empty packages * moves files to packages * adapt usages * updating READMEs and packages jsons * [CI] Auto-commit changed files from 'node scripts/generate codeowners' * adapt more usages * more import fixes * fix mock method names * export the `Fonts` component for security... * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * fix more usages again * includes tsx files Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 3 + package.json | 6 + packages/BUILD.bazel | 6 + .../BUILD.bazel | 107 +++++++++++++++ .../README.md | 3 + .../index.ts | 9 ++ .../jest.config.js | 13 ++ .../kibana.jsonc | 7 + .../package.json | 8 ++ .../src/index.ts | 9 ++ .../src/types.ts | 51 +++++++ .../tsconfig.json | 17 +++ .../BUILD.bazel | 127 ++++++++++++++++++ .../core-rendering-server-internal/README.md | 3 + .../core-rendering-server-internal/index.ts | 17 +++ .../jest.config.js | 13 ++ .../kibana.jsonc | 7 + .../package.json | 8 ++ .../rendering_service.test.ts.snap | 0 .../render_template.test.ts.snap | 0 .../bootstrap_renderer.test.mocks.ts | 0 .../src}/bootstrap/bootstrap_renderer.test.ts | 2 +- .../src}/bootstrap/bootstrap_renderer.ts | 2 +- .../bootstrap/get_js_dependency_paths.test.ts | 0 .../src}/bootstrap/get_js_dependency_paths.ts | 0 .../bootstrap/get_plugin_bundle_paths.test.ts | 3 +- .../src}/bootstrap/get_plugin_bundle_paths.ts | 2 +- .../src}/bootstrap/get_theme_tag.test.ts | 0 .../src}/bootstrap/get_theme_tag.ts | 0 .../src}/bootstrap/index.ts | 0 .../bootstrap/register_bootstrap_route.ts | 0 .../src}/bootstrap/render_template.test.ts | 0 .../src}/bootstrap/render_template.ts | 0 .../src}/filter_ui_plugins.test.ts | 3 +- .../src}/filter_ui_plugins.ts | 2 +- .../src}/index.ts | 1 + .../src}/internal_types.ts | 0 .../src}/render_utils.test.ts | 0 .../src}/render_utils.ts | 0 .../src}/rendering_service.test.mocks.ts | 0 .../src}/rendering_service.test.ts | 2 +- .../src}/rendering_service.tsx | 4 +- .../src/test_helpers}/params.ts | 13 +- .../src/test_helpers}/rendering_service.ts | 0 .../src}/types.ts | 4 +- .../src}/views/fonts.tsx | 0 .../src}/views/index.ts | 1 + .../src}/views/logo.tsx | 0 .../src}/views/styles.tsx | 0 .../src}/views/template.tsx | 0 .../tsconfig.json | 19 +++ .../core-rendering-server-mocks/BUILD.bazel | 106 +++++++++++++++ .../core-rendering-server-mocks/README.md | 4 + .../core-rendering-server-mocks/index.ts | 9 ++ .../jest.config.js | 13 ++ .../core-rendering-server-mocks/kibana.jsonc | 7 + .../core-rendering-server-mocks/package.json | 8 ++ .../core-rendering-server-mocks/src/index.ts | 9 ++ .../src}/rendering_service.mock.ts | 25 +++- .../core-rendering-server-mocks/tsconfig.json | 17 +++ .../register_bundle_routes.test.ts | 2 +- .../bundle_routes/register_bundle_routes.ts | 2 +- src/core/server/core_app/core_app.test.ts | 2 +- src/core/server/core_app/core_app.ts | 2 +- .../http_resources_service.test.ts | 6 +- .../http_resources/http_resources_service.ts | 5 +- src/core/server/index.ts | 1 - src/core/server/internal_types.ts | 2 +- src/core/server/mocks.ts | 6 +- src/core/server/plugins/index.ts | 1 - src/core/server/plugins/plugins_service.ts | 28 +--- src/core/server/plugins/types.ts | 22 --- src/core/server/server.test.mocks.ts | 8 +- src/core/server/server.ts | 2 +- .../unauthenticated_page.test.tsx | 2 +- .../authorization/reset_session_page.test.tsx | 2 +- .../security/server/prompt_page.test.tsx | 2 +- .../plugins/security/server/prompt_page.tsx | 2 +- yarn.lock | 24 ++++ 79 files changed, 705 insertions(+), 86 deletions(-) create mode 100644 packages/core/plugins/core-plugins-base-server-internal/BUILD.bazel create mode 100644 packages/core/plugins/core-plugins-base-server-internal/README.md create mode 100644 packages/core/plugins/core-plugins-base-server-internal/index.ts create mode 100644 packages/core/plugins/core-plugins-base-server-internal/jest.config.js create mode 100644 packages/core/plugins/core-plugins-base-server-internal/kibana.jsonc create mode 100644 packages/core/plugins/core-plugins-base-server-internal/package.json create mode 100644 packages/core/plugins/core-plugins-base-server-internal/src/index.ts create mode 100644 packages/core/plugins/core-plugins-base-server-internal/src/types.ts create mode 100644 packages/core/plugins/core-plugins-base-server-internal/tsconfig.json create mode 100644 packages/core/rendering/core-rendering-server-internal/BUILD.bazel create mode 100644 packages/core/rendering/core-rendering-server-internal/README.md create mode 100644 packages/core/rendering/core-rendering-server-internal/index.ts create mode 100644 packages/core/rendering/core-rendering-server-internal/jest.config.js create mode 100644 packages/core/rendering/core-rendering-server-internal/kibana.jsonc create mode 100644 packages/core/rendering/core-rendering-server-internal/package.json rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/__snapshots__/rendering_service.test.ts.snap (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/__snapshots__/render_template.test.ts.snap (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/bootstrap_renderer.test.mocks.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/bootstrap_renderer.test.ts (98%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/bootstrap_renderer.ts (97%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_js_dependency_paths.test.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_js_dependency_paths.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_plugin_bundle_paths.test.ts (94%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_plugin_bundle_paths.ts (95%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_theme_tag.test.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/get_theme_tag.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/index.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/register_bootstrap_route.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/render_template.test.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/bootstrap/render_template.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/filter_ui_plugins.test.ts (93%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/filter_ui_plugins.ts (95%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/index.ts (94%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/internal_types.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/render_utils.test.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/render_utils.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/rendering_service.test.mocks.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/rendering_service.test.ts (99%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/rendering_service.tsx (98%) rename {src/core/server/rendering/__mocks__ => packages/core/rendering/core-rendering-server-internal/src/test_helpers}/params.ts (85%) rename {src/core/server/rendering/__mocks__ => packages/core/rendering/core-rendering-server-internal/src/test_helpers}/rendering_service.ts (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/types.ts (96%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/views/fonts.tsx (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/views/index.ts (92%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/views/logo.tsx (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/views/styles.tsx (100%) rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-internal/src}/views/template.tsx (100%) create mode 100644 packages/core/rendering/core-rendering-server-internal/tsconfig.json create mode 100644 packages/core/rendering/core-rendering-server-mocks/BUILD.bazel create mode 100644 packages/core/rendering/core-rendering-server-mocks/README.md create mode 100644 packages/core/rendering/core-rendering-server-mocks/index.ts create mode 100644 packages/core/rendering/core-rendering-server-mocks/jest.config.js create mode 100644 packages/core/rendering/core-rendering-server-mocks/kibana.jsonc create mode 100644 packages/core/rendering/core-rendering-server-mocks/package.json create mode 100644 packages/core/rendering/core-rendering-server-mocks/src/index.ts rename {src/core/server/rendering => packages/core/rendering/core-rendering-server-mocks/src}/rendering_service.mock.ts (55%) create mode 100644 packages/core/rendering/core-rendering-server-mocks/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a1fcc60d55b..b7d588bc8926 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -784,6 +784,7 @@ packages/core/notifications/core-notifications-browser-mocks @elastic/kibana-cor packages/core/overlays/core-overlays-browser @elastic/kibana-core packages/core/overlays/core-overlays-browser-internal @elastic/kibana-core packages/core/overlays/core-overlays-browser-mocks @elastic/kibana-core +packages/core/plugins/core-plugins-base-server-internal @elastic/kibana-core packages/core/plugins/core-plugins-browser @elastic/kibana-core packages/core/plugins/core-plugins-browser-internal @elastic/kibana-core packages/core/plugins/core-plugins-browser-mocks @elastic/kibana-core @@ -792,6 +793,8 @@ packages/core/preboot/core-preboot-server-internal @elastic/kibana-core packages/core/preboot/core-preboot-server-mocks @elastic/kibana-core packages/core/rendering/core-rendering-browser-internal @elastic/kibana-core packages/core/rendering/core-rendering-browser-mocks @elastic/kibana-core +packages/core/rendering/core-rendering-server-internal @elastic/kibana-core +packages/core/rendering/core-rendering-server-mocks @elastic/kibana-core packages/core/root/core-root-browser-internal @elastic/kibana-core packages/core/saved-objects/core-saved-objects-api-browser @elastic/kibana-core packages/core/saved-objects/core-saved-objects-api-server @elastic/kibana-core diff --git a/package.json b/package.json index e7b1f86b9c05..4108e83b0e6c 100644 --- a/package.json +++ b/package.json @@ -254,6 +254,7 @@ "@kbn/core-overlays-browser": "link:bazel-bin/packages/core/overlays/core-overlays-browser", "@kbn/core-overlays-browser-internal": "link:bazel-bin/packages/core/overlays/core-overlays-browser-internal", "@kbn/core-overlays-browser-mocks": "link:bazel-bin/packages/core/overlays/core-overlays-browser-mocks", + "@kbn/core-plugins-base-server-internal": "link:bazel-bin/packages/core/plugins/core-plugins-base-server-internal", "@kbn/core-plugins-browser": "link:bazel-bin/packages/core/plugins/core-plugins-browser", "@kbn/core-plugins-browser-internal": "link:bazel-bin/packages/core/plugins/core-plugins-browser-internal", "@kbn/core-plugins-browser-mocks": "link:bazel-bin/packages/core/plugins/core-plugins-browser-mocks", @@ -262,6 +263,8 @@ "@kbn/core-preboot-server-mocks": "link:bazel-bin/packages/core/preboot/core-preboot-server-mocks", "@kbn/core-rendering-browser-internal": "link:bazel-bin/packages/core/rendering/core-rendering-browser-internal", "@kbn/core-rendering-browser-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks", + "@kbn/core-rendering-server-internal": "link:bazel-bin/packages/core/rendering/core-rendering-server-internal", + "@kbn/core-rendering-server-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-server-mocks", "@kbn/core-root-browser-internal": "link:bazel-bin/packages/core/root/core-root-browser-internal", "@kbn/core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser", "@kbn/core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server", @@ -978,6 +981,7 @@ "@types/kbn__core-overlays-browser": "link:bazel-bin/packages/core/overlays/core-overlays-browser/npm_module_types", "@types/kbn__core-overlays-browser-internal": "link:bazel-bin/packages/core/overlays/core-overlays-browser-internal/npm_module_types", "@types/kbn__core-overlays-browser-mocks": "link:bazel-bin/packages/core/overlays/core-overlays-browser-mocks/npm_module_types", + "@types/kbn__core-plugins-base-server-internal": "link:bazel-bin/packages/core/plugins/core-plugins-base-server-internal/npm_module_types", "@types/kbn__core-plugins-browser": "link:bazel-bin/packages/core/plugins/core-plugins-browser/npm_module_types", "@types/kbn__core-plugins-browser-internal": "link:bazel-bin/packages/core/plugins/core-plugins-browser-internal/npm_module_types", "@types/kbn__core-plugins-browser-mocks": "link:bazel-bin/packages/core/plugins/core-plugins-browser-mocks/npm_module_types", @@ -987,6 +991,8 @@ "@types/kbn__core-public-internal-base": "link:bazel-bin/packages/core/public/internal-base/npm_module_types", "@types/kbn__core-rendering-browser-internal": "link:bazel-bin/packages/core/rendering/core-rendering-browser-internal/npm_module_types", "@types/kbn__core-rendering-browser-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-browser-mocks/npm_module_types", + "@types/kbn__core-rendering-server-internal": "link:bazel-bin/packages/core/rendering/core-rendering-server-internal/npm_module_types", + "@types/kbn__core-rendering-server-mocks": "link:bazel-bin/packages/core/rendering/core-rendering-server-mocks/npm_module_types", "@types/kbn__core-root-browser-internal": "link:bazel-bin/packages/core/root/core-root-browser-internal/npm_module_types", "@types/kbn__core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser/npm_module_types", "@types/kbn__core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index cf97e501df09..0d5ecd4bc4cf 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -120,6 +120,7 @@ filegroup( "//packages/core/overlays/core-overlays-browser:build", "//packages/core/overlays/core-overlays-browser-internal:build", "//packages/core/overlays/core-overlays-browser-mocks:build", + "//packages/core/plugins/core-plugins-base-server-internal:build", "//packages/core/plugins/core-plugins-browser:build", "//packages/core/plugins/core-plugins-browser-internal:build", "//packages/core/plugins/core-plugins-browser-mocks:build", @@ -128,6 +129,8 @@ filegroup( "//packages/core/preboot/core-preboot-server-mocks:build", "//packages/core/rendering/core-rendering-browser-internal:build", "//packages/core/rendering/core-rendering-browser-mocks:build", + "//packages/core/rendering/core-rendering-server-internal:build", + "//packages/core/rendering/core-rendering-server-mocks:build", "//packages/core/root/core-root-browser-internal:build", "//packages/core/saved-objects/core-saved-objects-api-browser:build", "//packages/core/saved-objects/core-saved-objects-api-server:build", @@ -454,6 +457,7 @@ filegroup( "//packages/core/overlays/core-overlays-browser:build_types", "//packages/core/overlays/core-overlays-browser-internal:build_types", "//packages/core/overlays/core-overlays-browser-mocks:build_types", + "//packages/core/plugins/core-plugins-base-server-internal:build_types", "//packages/core/plugins/core-plugins-browser:build_types", "//packages/core/plugins/core-plugins-browser-internal:build_types", "//packages/core/plugins/core-plugins-browser-mocks:build_types", @@ -462,6 +466,8 @@ filegroup( "//packages/core/preboot/core-preboot-server-mocks:build_types", "//packages/core/rendering/core-rendering-browser-internal:build_types", "//packages/core/rendering/core-rendering-browser-mocks:build_types", + "//packages/core/rendering/core-rendering-server-internal:build_types", + "//packages/core/rendering/core-rendering-server-mocks:build_types", "//packages/core/root/core-root-browser-internal:build_types", "//packages/core/saved-objects/core-saved-objects-api-browser:build_types", "//packages/core/saved-objects/core-saved-objects-api-server:build_types", diff --git a/packages/core/plugins/core-plugins-base-server-internal/BUILD.bazel b/packages/core/plugins/core-plugins-base-server-internal/BUILD.bazel new file mode 100644 index 000000000000..7e4d73b638a7 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/BUILD.bazel @@ -0,0 +1,107 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-plugins-base-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-plugins-base-server-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/core/base/core-base-common:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/plugins/core-plugins-base-server-internal/README.md b/packages/core/plugins/core-plugins-base-server-internal/README.md new file mode 100644 index 000000000000..565082ebccc0 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-plugins-base-server-internal + +This package contains base internal types of the `plugins` domain used across other core domains. diff --git a/packages/core/plugins/core-plugins-base-server-internal/index.ts b/packages/core/plugins/core-plugins-base-server-internal/index.ts new file mode 100644 index 000000000000..3052f46e9fe7 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { UiPlugins, InternalPluginInfo } from './src'; diff --git a/packages/core/plugins/core-plugins-base-server-internal/jest.config.js b/packages/core/plugins/core-plugins-base-server-internal/jest.config.js new file mode 100644 index 000000000000..9a9b5aa5ec9b --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/plugins/core-plugins-base-server-internal'], +}; diff --git a/packages/core/plugins/core-plugins-base-server-internal/kibana.jsonc b/packages/core/plugins/core-plugins-base-server-internal/kibana.jsonc new file mode 100644 index 000000000000..a593530ab5fc --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-plugins-base-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/plugins/core-plugins-base-server-internal/package.json b/packages/core/plugins/core-plugins-base-server-internal/package.json new file mode 100644 index 000000000000..6af3453f1a29 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-plugins-base-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/plugins/core-plugins-base-server-internal/src/index.ts b/packages/core/plugins/core-plugins-base-server-internal/src/index.ts new file mode 100644 index 000000000000..ade9e77fc178 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { UiPlugins, InternalPluginInfo } from './types'; diff --git a/packages/core/plugins/core-plugins-base-server-internal/src/types.ts b/packages/core/plugins/core-plugins-base-server-internal/src/types.ts new file mode 100644 index 000000000000..d0afcce5dba5 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/src/types.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Observable } from 'rxjs'; +import type { DiscoveredPlugin, PluginName } from '@kbn/core-base-common'; + +/** @internal */ +export interface UiPlugins { + /** + * Paths to all discovered ui plugin entrypoints on the filesystem, even if + * disabled. + */ + internal: Map; + + /** + * Information needed by client-side to load plugins and wire dependencies. + */ + public: Map; + + /** + * Configuration for plugins to be exposed to the client-side. + */ + browserConfigs: Map>; +} + +/** + * @internal + */ +export interface InternalPluginInfo { + /** + * Version of the plugin + */ + readonly version: string; + /** + * Bundles that must be loaded for this plugin + */ + readonly requiredBundles: readonly string[]; + /** + * Path to the target/public directory of the plugin which should be served + */ + readonly publicTargetDir: string; + /** + * Path to the plugin assets directory. + */ + readonly publicAssetsDir: string; +} diff --git a/packages/core/plugins/core-plugins-base-server-internal/tsconfig.json b/packages/core/plugins/core-plugins-base-server-internal/tsconfig.json new file mode 100644 index 000000000000..71bb40fe57f3 --- /dev/null +++ b/packages/core/plugins/core-plugins-base-server-internal/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/core/rendering/core-rendering-server-internal/BUILD.bazel b/packages/core/rendering/core-rendering-server-internal/BUILD.bazel new file mode 100644 index 000000000000..b02ff0926469 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/BUILD.bazel @@ -0,0 +1,127 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-rendering-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-rendering-server-internal" + +SOURCE_FILES = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//react", + "@npm//react-dom", + "@npm//rxjs", + "//packages/kbn-i18n", + "//packages/kbn-ui-shared-deps-npm", + "//packages/kbn-ui-shared-deps-src", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "@npm//@types/react-dom", + "@npm//rxjs", + "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-ui-shared-deps-npm:npm_module_types", + "//packages/kbn-ui-shared-deps-src:npm_module_types", + "//packages/core/base/core-base-server-internal:npm_module_types", + "//packages/core/injected-metadata/core-injected-metadata-common-internal:npm_module_types", + "//packages/core/http/core-http-server:npm_module_types", + "//packages/core/http/core-http-server-internal:npm_module_types", + "//packages/core/elasticsearch/core-elasticsearch-server-internal:npm_module_types", + "//packages/core/status/core-status-server-internal:npm_module_types", + "//packages/core/ui-settings/core-ui-settings-common:npm_module_types", + "//packages/core/ui-settings/core-ui-settings-server:npm_module_types", + "//packages/core/plugins/core-plugins-base-server-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/rendering/core-rendering-server-internal/README.md b/packages/core/rendering/core-rendering-server-internal/README.md new file mode 100644 index 000000000000..629ac2047de0 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-rendering-server-internal + +This package contains the internal types and implementation for Core's server-side rendering service. diff --git a/packages/core/rendering/core-rendering-server-internal/index.ts b/packages/core/rendering/core-rendering-server-internal/index.ts new file mode 100644 index 000000000000..7ddc442a7425 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { RenderingService, Fonts } from './src'; +export type { + InternalRenderingServicePreboot, + InternalRenderingServiceSetup, + IRenderOptions, + RenderingMetadata, + RenderingPrebootDeps, + RenderingSetupDeps, +} from './src'; diff --git a/packages/core/rendering/core-rendering-server-internal/jest.config.js b/packages/core/rendering/core-rendering-server-internal/jest.config.js new file mode 100644 index 000000000000..48ca2e89b976 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/rendering/core-rendering-server-internal'], +}; diff --git a/packages/core/rendering/core-rendering-server-internal/kibana.jsonc b/packages/core/rendering/core-rendering-server-internal/kibana.jsonc new file mode 100644 index 000000000000..2ce227d70528 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-rendering-server-internal", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/rendering/core-rendering-server-internal/package.json b/packages/core/rendering/core-rendering-server-internal/package.json new file mode 100644 index 000000000000..ef29d29e9fa2 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-rendering-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap similarity index 100% rename from src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap rename to packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap diff --git a/src/core/server/rendering/bootstrap/__snapshots__/render_template.test.ts.snap b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/__snapshots__/render_template.test.ts.snap similarity index 100% rename from src/core/server/rendering/bootstrap/__snapshots__/render_template.test.ts.snap rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/__snapshots__/render_template.test.ts.snap diff --git a/src/core/server/rendering/bootstrap/bootstrap_renderer.test.mocks.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.mocks.ts similarity index 100% rename from src/core/server/rendering/bootstrap/bootstrap_renderer.test.mocks.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.mocks.ts diff --git a/src/core/server/rendering/bootstrap/bootstrap_renderer.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts similarity index 98% rename from src/core/server/rendering/bootstrap/bootstrap_renderer.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts index a1ed415f72c4..af9de3fbd6ff 100644 --- a/src/core/server/rendering/bootstrap/bootstrap_renderer.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts @@ -15,7 +15,7 @@ import { import { PackageInfo } from '@kbn/config'; import { AuthStatus } from '@kbn/core-http-server'; -import { UiPlugins } from '../../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; import { bootstrapRendererFactory, BootstrapRenderer } from './bootstrap_renderer'; diff --git a/src/core/server/rendering/bootstrap/bootstrap_renderer.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts similarity index 97% rename from src/core/server/rendering/bootstrap/bootstrap_renderer.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts index 4c20f8d3d799..8424bb3e68a1 100644 --- a/src/core/server/rendering/bootstrap/bootstrap_renderer.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts @@ -11,7 +11,7 @@ import { PackageInfo } from '@kbn/config'; import { ThemeVersion } from '@kbn/ui-shared-deps-npm'; import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; -import { UiPlugins } from '../../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { getPluginsBundlePaths } from './get_plugin_bundle_paths'; import { getJsDependencyPaths } from './get_js_dependency_paths'; import { getThemeTag } from './get_theme_tag'; diff --git a/src/core/server/rendering/bootstrap/get_js_dependency_paths.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_js_dependency_paths.test.ts similarity index 100% rename from src/core/server/rendering/bootstrap/get_js_dependency_paths.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_js_dependency_paths.test.ts diff --git a/src/core/server/rendering/bootstrap/get_js_dependency_paths.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_js_dependency_paths.ts similarity index 100% rename from src/core/server/rendering/bootstrap/get_js_dependency_paths.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_js_dependency_paths.ts diff --git a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.test.ts similarity index 94% rename from src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.test.ts index e3746eeb17a0..619765cdc7b6 100644 --- a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.test.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { InternalPluginInfo, PluginType, UiPlugins } from '../../plugins'; +import { PluginType } from '@kbn/core-base-common'; +import type { InternalPluginInfo, UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { getPluginsBundlePaths } from './get_plugin_bundle_paths'; const createUiPlugins = (pluginDeps: Record) => { diff --git a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.ts similarity index 95% rename from src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.ts index e5a9ed9fdd3d..ad9f3edb4aa5 100644 --- a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_plugin_bundle_paths.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { UiPlugins } from '../../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { filterUiPlugins } from '../filter_ui_plugins'; export interface PluginInfo { diff --git a/src/core/server/rendering/bootstrap/get_theme_tag.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_theme_tag.test.ts similarity index 100% rename from src/core/server/rendering/bootstrap/get_theme_tag.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_theme_tag.test.ts diff --git a/src/core/server/rendering/bootstrap/get_theme_tag.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_theme_tag.ts similarity index 100% rename from src/core/server/rendering/bootstrap/get_theme_tag.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/get_theme_tag.ts diff --git a/src/core/server/rendering/bootstrap/index.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/index.ts similarity index 100% rename from src/core/server/rendering/bootstrap/index.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/index.ts diff --git a/src/core/server/rendering/bootstrap/register_bootstrap_route.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/register_bootstrap_route.ts similarity index 100% rename from src/core/server/rendering/bootstrap/register_bootstrap_route.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/register_bootstrap_route.ts diff --git a/src/core/server/rendering/bootstrap/render_template.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/render_template.test.ts similarity index 100% rename from src/core/server/rendering/bootstrap/render_template.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/render_template.test.ts diff --git a/src/core/server/rendering/bootstrap/render_template.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/render_template.ts similarity index 100% rename from src/core/server/rendering/bootstrap/render_template.ts rename to packages/core/rendering/core-rendering-server-internal/src/bootstrap/render_template.ts diff --git a/src/core/server/rendering/filter_ui_plugins.test.ts b/packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.test.ts similarity index 93% rename from src/core/server/rendering/filter_ui_plugins.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.test.ts index fc013b4be0d0..096e0cc57b1f 100644 --- a/src/core/server/rendering/filter_ui_plugins.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.test.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import type { DiscoveredPlugin, PluginName, UiPlugins } from '../plugins'; +import type { PluginName, DiscoveredPlugin } from '@kbn/core-base-common'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { filterUiPlugins } from './filter_ui_plugins'; function createMockPlugin(params: Partial) { diff --git a/src/core/server/rendering/filter_ui_plugins.ts b/packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.ts similarity index 95% rename from src/core/server/rendering/filter_ui_plugins.ts rename to packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.ts index d3ca102a3575..e1be7719bfea 100644 --- a/src/core/server/rendering/filter_ui_plugins.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/filter_ui_plugins.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { UiPlugins } from '../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; /** * Gets the array of plugins that should be enabled on the page. diff --git a/src/core/server/rendering/index.ts b/packages/core/rendering/core-rendering-server-internal/src/index.ts similarity index 94% rename from src/core/server/rendering/index.ts rename to packages/core/rendering/core-rendering-server-internal/src/index.ts index 6cf0e2a74aa1..e1cac45f7765 100644 --- a/src/core/server/rendering/index.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/index.ts @@ -7,6 +7,7 @@ */ export { RenderingService } from './rendering_service'; +export { Fonts } from './views'; export type { InternalRenderingServicePreboot, InternalRenderingServiceSetup, diff --git a/src/core/server/rendering/internal_types.ts b/packages/core/rendering/core-rendering-server-internal/src/internal_types.ts similarity index 100% rename from src/core/server/rendering/internal_types.ts rename to packages/core/rendering/core-rendering-server-internal/src/internal_types.ts diff --git a/src/core/server/rendering/render_utils.test.ts b/packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts similarity index 100% rename from src/core/server/rendering/render_utils.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/render_utils.test.ts diff --git a/src/core/server/rendering/render_utils.ts b/packages/core/rendering/core-rendering-server-internal/src/render_utils.ts similarity index 100% rename from src/core/server/rendering/render_utils.ts rename to packages/core/rendering/core-rendering-server-internal/src/render_utils.ts diff --git a/src/core/server/rendering/rendering_service.test.mocks.ts b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts similarity index 100% rename from src/core/server/rendering/rendering_service.test.mocks.ts rename to packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts diff --git a/src/core/server/rendering/rendering_service.test.ts b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts similarity index 99% rename from src/core/server/rendering/rendering_service.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts index 424bcd8a6533..48196717c6f9 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts @@ -22,7 +22,7 @@ import { mockRenderingServiceParams, mockRenderingPrebootDeps, mockRenderingSetupDeps, -} from './__mocks__/params'; +} from './test_helpers/params'; import { InternalRenderingServicePreboot, InternalRenderingServiceSetup } from './types'; import { RenderingService } from './rendering_service'; import { AuthStatus } from '@kbn/core-http-server'; diff --git a/src/core/server/rendering/rendering_service.tsx b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx similarity index 98% rename from src/core/server/rendering/rendering_service.tsx rename to packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx index c5b56a06782b..653768c83d78 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx @@ -8,15 +8,15 @@ import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; +import { firstValueFrom, of } from 'rxjs'; import { catchError, take, timeout } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import type { ThemeVersion } from '@kbn/ui-shared-deps-npm'; -import { firstValueFrom, of } from 'rxjs'; import type { CoreContext } from '@kbn/core-base-server-internal'; import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; -import type { UiPlugins } from '../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { Template } from './views'; import { IRenderOptions, diff --git a/src/core/server/rendering/__mocks__/params.ts b/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts similarity index 85% rename from src/core/server/rendering/__mocks__/params.ts rename to packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts index c75353b87a65..51dfab71efd2 100644 --- a/src/core/server/rendering/__mocks__/params.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts @@ -10,7 +10,6 @@ import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { statusServiceMock } from '@kbn/core-status-server-mocks'; -import { pluginServiceMock } from '../../plugins/plugins_service.mock'; const context = mockCoreContext.create(); const httpPreboot = httpServiceMock.createInternalPrebootContract(); @@ -18,14 +17,22 @@ const httpSetup = httpServiceMock.createInternalSetupContract(); const status = statusServiceMock.createInternalSetupContract(); const elasticsearch = elasticsearchServiceMock.createInternalSetup(); +function createUiPlugins() { + return { + browserConfigs: new Map(), + internal: new Map(), + public: new Map(), + }; +} + export const mockRenderingServiceParams = context; export const mockRenderingPrebootDeps = { http: httpPreboot, - uiPlugins: pluginServiceMock.createUiPlugins(), + uiPlugins: createUiPlugins(), }; export const mockRenderingSetupDeps = { elasticsearch, http: httpSetup, - uiPlugins: pluginServiceMock.createUiPlugins(), + uiPlugins: createUiPlugins(), status, }; diff --git a/src/core/server/rendering/__mocks__/rendering_service.ts b/packages/core/rendering/core-rendering-server-internal/src/test_helpers/rendering_service.ts similarity index 100% rename from src/core/server/rendering/__mocks__/rendering_service.ts rename to packages/core/rendering/core-rendering-server-internal/src/test_helpers/rendering_service.ts diff --git a/src/core/server/rendering/types.ts b/packages/core/rendering/core-rendering-server-internal/src/types.ts similarity index 96% rename from src/core/server/rendering/types.ts rename to packages/core/rendering/core-rendering-server-internal/src/types.ts index 7068d2a9b789..e2f7797ac5c4 100644 --- a/src/core/server/rendering/types.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/types.ts @@ -17,7 +17,7 @@ import type { import type { InternalElasticsearchServiceSetup } from '@kbn/core-elasticsearch-server-internal'; import type { InternalStatusServiceSetup } from '@kbn/core-status-server-internal'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; -import { UiPlugins } from '../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; /** @internal */ export interface RenderingMetadata { @@ -46,7 +46,7 @@ export interface RenderingSetupDeps { uiPlugins: UiPlugins; } -/** @public */ +/** @internal */ export interface IRenderOptions { /** * Set whether the page is anonymous, which determines what plugins are enabled and whether to output user settings in the page metadata. diff --git a/src/core/server/rendering/views/fonts.tsx b/packages/core/rendering/core-rendering-server-internal/src/views/fonts.tsx similarity index 100% rename from src/core/server/rendering/views/fonts.tsx rename to packages/core/rendering/core-rendering-server-internal/src/views/fonts.tsx diff --git a/src/core/server/rendering/views/index.ts b/packages/core/rendering/core-rendering-server-internal/src/views/index.ts similarity index 92% rename from src/core/server/rendering/views/index.ts rename to packages/core/rendering/core-rendering-server-internal/src/views/index.ts index 1aa6e658e3d2..01f395261ecc 100644 --- a/src/core/server/rendering/views/index.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/views/index.ts @@ -7,3 +7,4 @@ */ export { Template } from './template'; +export { Fonts } from './fonts'; diff --git a/src/core/server/rendering/views/logo.tsx b/packages/core/rendering/core-rendering-server-internal/src/views/logo.tsx similarity index 100% rename from src/core/server/rendering/views/logo.tsx rename to packages/core/rendering/core-rendering-server-internal/src/views/logo.tsx diff --git a/src/core/server/rendering/views/styles.tsx b/packages/core/rendering/core-rendering-server-internal/src/views/styles.tsx similarity index 100% rename from src/core/server/rendering/views/styles.tsx rename to packages/core/rendering/core-rendering-server-internal/src/views/styles.tsx diff --git a/src/core/server/rendering/views/template.tsx b/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx similarity index 100% rename from src/core/server/rendering/views/template.tsx rename to packages/core/rendering/core-rendering-server-internal/src/views/template.tsx diff --git a/packages/core/rendering/core-rendering-server-internal/tsconfig.json b/packages/core/rendering/core-rendering-server-internal/tsconfig.json new file mode 100644 index 000000000000..73c8a6666ff7 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-internal/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ] +} diff --git a/packages/core/rendering/core-rendering-server-mocks/BUILD.bazel b/packages/core/rendering/core-rendering-server-mocks/BUILD.bazel new file mode 100644 index 000000000000..9ec36da1a1f6 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/BUILD.bazel @@ -0,0 +1,106 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-rendering-server-mocks" +PKG_REQUIRE_NAME = "@kbn/core-rendering-server-mocks" + +SOURCE_FILES = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/kbn-utility-types:npm_module_types", + "//packages/core/rendering/core-rendering-server-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/rendering/core-rendering-server-mocks/README.md b/packages/core/rendering/core-rendering-server-mocks/README.md new file mode 100644 index 000000000000..72df54931919 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/README.md @@ -0,0 +1,4 @@ +# @kbn/core-rendering-server-mocks + +This package contains mocks for Core's server-side rendering service. +- `renderingServiceMock` diff --git a/packages/core/rendering/core-rendering-server-mocks/index.ts b/packages/core/rendering/core-rendering-server-mocks/index.ts new file mode 100644 index 000000000000..e7ce72ce797f --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { renderingServiceMock } from './src'; diff --git a/packages/core/rendering/core-rendering-server-mocks/jest.config.js b/packages/core/rendering/core-rendering-server-mocks/jest.config.js new file mode 100644 index 000000000000..5dc1ae51c50b --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/rendering/core-rendering-server-mocks'], +}; diff --git a/packages/core/rendering/core-rendering-server-mocks/kibana.jsonc b/packages/core/rendering/core-rendering-server-mocks/kibana.jsonc new file mode 100644 index 000000000000..a04eae9cadc2 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/core-rendering-server-mocks", + "owner": "@elastic/kibana-core", + "runtimeDeps": [], + "typeDeps": [], +} diff --git a/packages/core/rendering/core-rendering-server-mocks/package.json b/packages/core/rendering/core-rendering-server-mocks/package.json new file mode 100644 index 000000000000..572e1d553058 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/core-rendering-server-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "author": "Kibana Core", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/rendering/core-rendering-server-mocks/src/index.ts b/packages/core/rendering/core-rendering-server-mocks/src/index.ts new file mode 100644 index 000000000000..d56794e3e9ac --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { renderingServiceMock } from './rendering_service.mock'; diff --git a/src/core/server/rendering/rendering_service.mock.ts b/packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts similarity index 55% rename from src/core/server/rendering/rendering_service.mock.ts rename to packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts index 3d8213da62c6..5cdf5c92f923 100644 --- a/src/core/server/rendering/rendering_service.mock.ts +++ b/packages/core/rendering/core-rendering-server-mocks/src/rendering_service.mock.ts @@ -6,7 +6,14 @@ * Side Public License, v 1. */ -import { InternalRenderingServicePreboot, InternalRenderingServiceSetup } from './types'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { + InternalRenderingServicePreboot, + InternalRenderingServiceSetup, + RenderingService, +} from '@kbn/core-rendering-server-internal'; + +export type RenderingServiceMock = jest.Mocked>; function createRenderingPreboot() { const mocked: jest.Mocked = { @@ -22,7 +29,21 @@ function createRenderingSetup() { return mocked; } -export const renderingMock = { +function createRenderingService() { + const mock: RenderingServiceMock = { + preboot: jest.fn(), + setup: jest.fn(), + stop: jest.fn(), + }; + + mock.preboot.mockResolvedValue(createRenderingPreboot()); + mock.setup.mockResolvedValue(createRenderingSetup()); + + return mock; +} + +export const renderingServiceMock = { + create: createRenderingService, createPrebootContract: createRenderingPreboot, createSetupContract: createRenderingSetup, }; diff --git a/packages/core/rendering/core-rendering-server-mocks/tsconfig.json b/packages/core/rendering/core-rendering-server-mocks/tsconfig.json new file mode 100644 index 000000000000..71bb40fe57f3 --- /dev/null +++ b/packages/core/rendering/core-rendering-server-mocks/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ] +} diff --git a/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts b/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts index 12f3041ec53f..f816a85404f7 100644 --- a/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts +++ b/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts @@ -10,7 +10,7 @@ import { registerRouteForBundleMock } from './register_bundle_routes.test.mocks' import { PackageInfo } from '@kbn/config'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; -import { InternalPluginInfo, UiPlugins } from '../../plugins'; +import type { InternalPluginInfo, UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { registerBundleRoutes } from './register_bundle_routes'; import { FileHashCache } from './file_hash_cache'; diff --git a/src/core/server/core_app/bundle_routes/register_bundle_routes.ts b/src/core/server/core_app/bundle_routes/register_bundle_routes.ts index 6f701b46dcc8..4adb50fdc96c 100644 --- a/src/core/server/core_app/bundle_routes/register_bundle_routes.ts +++ b/src/core/server/core_app/bundle_routes/register_bundle_routes.ts @@ -12,7 +12,7 @@ import { fromRoot } from '@kbn/utils'; import UiSharedDepsNpm from '@kbn/ui-shared-deps-npm'; import * as UiSharedDepsSrc from '@kbn/ui-shared-deps-src'; import type { IRouter } from '@kbn/core-http-server'; -import { UiPlugins } from '../../plugins'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { FileHashCache } from './file_hash_cache'; import { registerRouteForBundle } from './bundles_route'; diff --git a/src/core/server/core_app/core_app.test.ts b/src/core/server/core_app/core_app.test.ts index 913ac2aa9ef6..76663eeed2fd 100644 --- a/src/core/server/core_app/core_app.test.ts +++ b/src/core/server/core_app/core_app.test.ts @@ -10,9 +10,9 @@ import { registerBundleRoutesMock } from './core_app.test.mocks'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { mockRouter } from '@kbn/core-http-router-server-mocks'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { coreMock, httpServerMock } from '../mocks'; import { httpResourcesMock } from '../http_resources/http_resources_service.mock'; -import type { UiPlugins } from '../plugins'; import { PluginType } from '../plugins'; import { CoreApp } from './core_app'; import { RequestHandlerContext } from '..'; diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts index d6f305698fc9..b8701d7646b7 100644 --- a/src/core/server/core_app/core_app.ts +++ b/src/core/server/core_app/core_app.ts @@ -20,10 +20,10 @@ import type { KibanaRequest, IBasePath, } from '@kbn/core-http-server'; +import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { HttpResources, HttpResourcesServiceToolkit } from '../http_resources'; import { InternalCorePreboot, InternalCoreSetup } from '../internal_types'; import { registerBundleRoutes } from './bundle_routes'; -import { UiPlugins } from '../plugins'; import type { InternalCoreAppRequestHandlerContext } from './internal_types'; /** @internal */ diff --git a/src/core/server/http_resources/http_resources_service.test.ts b/src/core/server/http_resources/http_resources_service.test.ts index 47b0ef049485..ea04f3084750 100644 --- a/src/core/server/http_resources/http_resources_service.test.ts +++ b/src/core/server/http_resources/http_resources_service.test.ts @@ -13,7 +13,7 @@ import type { RouteConfig } from '@kbn/core-http-server'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks'; import { coreMock } from '../mocks'; -import { renderingMock } from '../rendering/rendering_service.mock'; +import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; import { HttpResourcesService, PrebootDeps, SetupDeps } from './http_resources_service'; import { httpResourcesMock } from './http_resources_service.mock'; import { HttpResources } from '..'; @@ -37,11 +37,11 @@ describe('HttpResources service', () => { beforeEach(() => { prebootDeps = { http: httpServiceMock.createInternalPrebootContract(), - rendering: renderingMock.createPrebootContract(), + rendering: renderingServiceMock.createPrebootContract(), }; setupDeps = { http: httpServiceMock.createInternalSetupContract(), - rendering: renderingMock.createSetupContract(), + rendering: renderingServiceMock.createSetupContract(), }; service = new HttpResourcesService(coreContext); router = httpServiceMock.createRouter(); diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts index 5db20bf45e40..7cc88699ea7b 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/src/core/server/http_resources/http_resources_service.ts @@ -18,8 +18,11 @@ import type { InternalHttpServiceSetup, InternalHttpServicePreboot, } from '@kbn/core-http-server-internal'; +import type { + InternalRenderingServicePreboot, + InternalRenderingServiceSetup, +} from '@kbn/core-rendering-server-internal'; import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; -import { InternalRenderingServicePreboot, InternalRenderingServiceSetup } from '../rendering'; import { InternalHttpResourcesSetup, HttpResources, diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 020975c15fb1..dafd53e374fe 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -230,7 +230,6 @@ export type { HttpResourcesRequestHandler, } from './http_resources'; -export type { IRenderOptions } from './rendering'; export type { LoggingServiceSetup, LoggerContextConfigInput, diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index e744ae01d1be..683d08fe4f84 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -58,7 +58,7 @@ import type { InternalUiSettingsServiceSetup, InternalUiSettingsServiceStart, } from '@kbn/core-ui-settings-server-internal'; -import { InternalRenderingServiceSetup } from './rendering'; +import type { InternalRenderingServiceSetup } from '@kbn/core-rendering-server-internal'; import { InternalHttpResourcesPreboot, InternalHttpResourcesSetup } from './http_resources'; /** @internal */ diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 6c2ef712cbf5..cd0429415e7c 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -31,6 +31,7 @@ import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { i18nServiceMock } from '@kbn/core-i18n-server-mocks'; import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; +import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; import type { PluginInitializerContext, CoreSetup, @@ -40,7 +41,6 @@ import type { RequestHandlerContext, } from '.'; import { httpResourcesMock } from './http_resources/http_resources_service.mock'; -import { renderingMock } from './rendering/rendering_service.mock'; import { SharedGlobalConfig } from './plugins'; export { configServiceMock, configDeprecationsMock } from '@kbn/config-mocks'; @@ -57,7 +57,7 @@ export { export { migrationMocks } from '@kbn/core-saved-objects-migration-server-mocks'; export { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; export { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; -export { renderingMock } from './rendering/rendering_service.mock'; +export { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; export { statusServiceMock } from '@kbn/core-status-server-mocks'; export { contextServiceMock } from '@kbn/core-http-context-server-mocks'; export { capabilitiesServiceMock } from '@kbn/core-capabilities-server-mocks'; @@ -237,7 +237,7 @@ function createInternalCoreSetupMock() { environment: environmentServiceMock.createSetupContract(), i18n: i18nServiceMock.createSetupContract(), httpResources: httpResourcesMock.createSetupContract(), - rendering: renderingMock.createSetupContract(), + rendering: renderingServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), metrics: metricsServiceMock.createInternalSetupContract(), diff --git a/src/core/server/plugins/index.ts b/src/core/server/plugins/index.ts index 1b655ccd8bd9..2111d467ef3c 100644 --- a/src/core/server/plugins/index.ts +++ b/src/core/server/plugins/index.ts @@ -10,7 +10,6 @@ export { PluginsService } from './plugins_service'; export type { PluginsServiceSetup, PluginsServiceStart, - UiPlugins, DiscoveredPlugins, } from './plugins_service'; export { config } from './plugins_config'; diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index a95619f3bc45..3305ff0a06b4 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -17,15 +17,10 @@ import type { CoreContext, CoreService } from '@kbn/core-base-server-internal'; import type { PluginName } from '@kbn/core-base-common'; import type { InternalEnvironmentServicePreboot } from '@kbn/core-environment-server-internal'; import type { InternalNodeServicePreboot } from '@kbn/core-node-server-internal'; +import type { InternalPluginInfo, UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery'; import { PluginWrapper } from './plugin'; -import { - DiscoveredPlugin, - InternalPluginInfo, - PluginConfigDescriptor, - PluginDependencies, - PluginType, -} from './types'; +import { DiscoveredPlugin, PluginConfigDescriptor, PluginDependencies, PluginType } from './types'; import { PluginsConfig, PluginsConfigType } from './plugins_config'; import { PluginsSystem } from './plugins_system'; import { createBrowserConfig } from './create_browser_config'; @@ -48,25 +43,6 @@ export interface PluginsServiceSetup { contracts: Map; } -/** @internal */ -export interface UiPlugins { - /** - * Paths to all discovered ui plugin entrypoints on the filesystem, even if - * disabled. - */ - internal: Map; - - /** - * Information needed by client-side to load plugins and wire dependencies. - */ - public: Map; - - /** - * Configuration for plugins to be exposed to the client-side. - */ - browserConfigs: Map>; -} - /** @internal */ export interface PluginsServiceStart { /** Start contracts returned by plugins. */ diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index f8b20b66f506..699631bc4411 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -254,28 +254,6 @@ export interface PluginManifest { readonly enabledOnAnonymousPages?: boolean; } -/** - * @internal - */ -export interface InternalPluginInfo { - /** - * Version of the plugin - */ - readonly version: string; - /** - * Bundles that must be loaded for this plugin - */ - readonly requiredBundles: readonly string[]; - /** - * Path to the target/public directory of the plugin which should be served - */ - readonly publicTargetDir: string; - /** - * Path to the plugin assets directory. - */ - readonly publicAssetsDir: string; -} - /** * The interface that should be returned by a `PluginInitializer` for a `preboot` plugin. * diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index dec2b17ae8c6..3e1d5c0e3a28 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -63,10 +63,12 @@ jest.doMock('@kbn/core-config-server-internal', () => ({ ensureValidConfiguration: mockEnsureValidConfiguration, })); -import { RenderingService, mockRenderingService } from './rendering/__mocks__/rendering_service'; +import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; -export { mockRenderingService }; -jest.doMock('./rendering/rendering_service', () => ({ RenderingService })); +export const mockRenderingService = renderingServiceMock.create(); +jest.doMock('@kbn/core-rendering-server-internal', () => ({ + RenderingService: jest.fn(() => mockRenderingService), +})); import { environmentServiceMock } from '@kbn/core-environment-server-mocks'; diff --git a/src/core/server/server.ts b/src/core/server/server.ts index e333e8b81a40..b7f41dd31dd0 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -66,10 +66,10 @@ import type { RequestHandlerContext, PrebootRequestHandlerContext, } from '@kbn/core-http-request-handler-context-server'; +import { RenderingService } from '@kbn/core-rendering-server-internal'; import { CoreApp } from './core_app'; import { HttpResourcesService } from './http_resources'; -import { RenderingService } from './rendering'; import { PluginsService, config as pluginsConfig } from './plugins'; import { InternalCorePreboot, InternalCoreSetup, InternalCoreStart } from './internal_types'; import { DiscoveredPlugins } from './plugins'; diff --git a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx index 708ca11ca8f7..aca2a5dd77e6 100644 --- a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx +++ b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx @@ -12,7 +12,7 @@ import { coreMock } from '@kbn/core/server/mocks'; import { UnauthenticatedPage } from './unauthenticated_page'; -jest.mock('@kbn/core/server/rendering/views/fonts', () => ({ +jest.mock('@kbn/core-rendering-server-internal', () => ({ Fonts: () => <>MockedFonts, })); diff --git a/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx b/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx index d5bce1e146eb..8111246f0776 100644 --- a/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx +++ b/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx @@ -12,7 +12,7 @@ import { coreMock } from '@kbn/core/server/mocks'; import { ResetSessionPage } from './reset_session_page'; -jest.mock('@kbn/core/server/rendering/views/fonts', () => ({ +jest.mock('@kbn/core-rendering-server-internal', () => ({ Fonts: () => <>MockedFonts, })); diff --git a/x-pack/plugins/security/server/prompt_page.test.tsx b/x-pack/plugins/security/server/prompt_page.test.tsx index 11b45f62f05d..ef59cacd31bf 100644 --- a/x-pack/plugins/security/server/prompt_page.test.tsx +++ b/x-pack/plugins/security/server/prompt_page.test.tsx @@ -12,7 +12,7 @@ import { coreMock } from '@kbn/core/server/mocks'; import { PromptPage } from './prompt_page'; -jest.mock('@kbn/core/server/rendering/views/fonts', () => ({ +jest.mock('@kbn/core-rendering-server-internal', () => ({ Fonts: () => <>MockedFonts, })); diff --git a/x-pack/plugins/security/server/prompt_page.tsx b/x-pack/plugins/security/server/prompt_page.tsx index e15574d5af4e..14f59df15db3 100644 --- a/x-pack/plugins/security/server/prompt_page.tsx +++ b/x-pack/plugins/security/server/prompt_page.tsx @@ -20,8 +20,8 @@ import createCache from '@emotion/cache'; import type { ReactNode } from 'react'; import React from 'react'; +import { Fonts } from '@kbn/core-rendering-server-internal'; import type { IBasePath } from '@kbn/core/server'; -import { Fonts } from '@kbn/core/server/rendering/views/fonts'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; import UiSharedDepsNpm from '@kbn/ui-shared-deps-npm'; diff --git a/yarn.lock b/yarn.lock index 968956a15ccf..b9db4636175d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3118,6 +3118,10 @@ version "0.0.0" uid "" +"@kbn/core-plugins-base-server-internal@link:bazel-bin/packages/core/plugins/core-plugins-base-server-internal": + version "0.0.0" + uid "" + "@kbn/core-plugins-browser-internal@link:bazel-bin/packages/core/plugins/core-plugins-browser-internal": version "0.0.0" uid "" @@ -3150,6 +3154,14 @@ version "0.0.0" uid "" +"@kbn/core-rendering-server-internal@link:bazel-bin/packages/core/rendering/core-rendering-server-internal": + version "0.0.0" + uid "" + +"@kbn/core-rendering-server-mocks@link:bazel-bin/packages/core/rendering/core-rendering-server-mocks": + version "0.0.0" + uid "" + "@kbn/core-root-browser-internal@link:bazel-bin/packages/core/root/core-root-browser-internal": version "0.0.0" uid "" @@ -7251,6 +7263,10 @@ version "0.0.0" uid "" +"@types/kbn__core-plugins-base-server-internal@link:bazel-bin/packages/core/plugins/core-plugins-base-server-internal/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-plugins-browser-internal@link:bazel-bin/packages/core/plugins/core-plugins-browser-internal/npm_module_types": version "0.0.0" uid "" @@ -7287,6 +7303,14 @@ version "0.0.0" uid "" +"@types/kbn__core-rendering-server-internal@link:bazel-bin/packages/core/rendering/core-rendering-server-internal/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__core-rendering-server-mocks@link:bazel-bin/packages/core/rendering/core-rendering-server-mocks/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-root-browser-internal@link:bazel-bin/packages/core/root/core-root-browser-internal/npm_module_types": version "0.0.0" uid "" From f8a90275d55257c8b08a35c267fbb189ef26d81e Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 3 Oct 2022 10:17:38 +0200 Subject: [PATCH 182/185] [Fleet] Bulk action refactor and small fixes (#142299) * refactor to report errors in agent update for all actions * fixed tests * fixed types * refactor to reduce duplication * fixed test * fix for cypress test * passing error reason Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/cypress/plugins/index.ts | 1 + .../components/tags_add_remove.tsx | 14 +++++- .../server/services/agents/action_status.ts | 5 +- .../server/services/agents/actions.test.ts | 12 +++-- .../fleet/server/services/agents/actions.ts | 50 +++++++++++++++---- .../fleet/server/services/agents/crud.ts | 21 ++++---- .../services/agents/reassign_action_runner.ts | 39 ++++----------- .../server/services/agents/unenroll.test.ts | 16 +++--- .../services/agents/unenroll_action_runner.ts | 45 +++++++---------- .../agents/update_agent_tags_action_runner.ts | 35 +++++-------- .../server/services/agents/upgrade.test.ts | 4 +- .../services/agents/upgrade_action_runner.ts | 47 +++++++---------- 12 files changed, 144 insertions(+), 145 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/plugins/index.ts b/x-pack/plugins/fleet/cypress/plugins/index.ts index d11dbb1e38ad..9fce88b6cd68 100644 --- a/x-pack/plugins/fleet/cypress/plugins/index.ts +++ b/x-pack/plugins/fleet/cypress/plugins/index.ts @@ -35,6 +35,7 @@ const plugin: Cypress.PluginConfig = (on, config) => { query, ignore_unavailable: ignoreUnavailable, refresh: true, + conflicts: 'proceed', }); }, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx index 70b4da44dad6..8307bc3467cc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags_add_remove.tsx @@ -123,12 +123,22 @@ export const TagsAddRemove: React.FC = ({ // sending updated tags to add/remove, in case multiple actions are done quickly and the first one is not yet propagated const updatedTagsToAdd = tagsToAdd.concat( labels - .filter((tag) => tag.checked === 'on' && !selectedTags.includes(tag.label)) + .filter( + (tag) => + tag.checked === 'on' && + !selectedTags.includes(tag.label) && + !tagsToRemove.includes(tag.label) + ) .map((tag) => tag.label) ); const updatedTagsToRemove = tagsToRemove.concat( labels - .filter((tag) => tag.checked !== 'on' && selectedTags.includes(tag.label)) + .filter( + (tag) => + tag.checked !== 'on' && + selectedTags.includes(tag.label) && + !tagsToAdd.includes(tag.label) + ) .map((tag) => tag.label) ); diff --git a/x-pack/plugins/fleet/server/services/agents/action_status.ts b/x-pack/plugins/fleet/server/services/agents/action_status.ts index 8489c25e3fd8..a057af185a06 100644 --- a/x-pack/plugins/fleet/server/services/agents/action_status.ts +++ b/x-pack/plugins/fleet/server/services/agents/action_status.ts @@ -64,7 +64,10 @@ export async function getActionStatuses( const matchingBucket = (acks?.aggregations?.ack_counts as any)?.buckets?.find( (bucket: any) => bucket.key === action.actionId ); - const nbAgentsAck = (matchingBucket?.agent_count as any)?.value ?? 0; + const nbAgentsAck = Math.min( + matchingBucket?.doc_count ?? 0, + (matchingBucket?.agent_count as any)?.value ?? 0 + ); const completionTime = (matchingBucket?.max_timestamp as any)?.value_as_string; const nbAgentsActioned = action.nbAgentsActioned || action.nbAgentsActionCreated; const complete = nbAgentsAck >= nbAgentsActioned; diff --git a/x-pack/plugins/fleet/server/services/agents/actions.test.ts b/x-pack/plugins/fleet/server/services/agents/actions.test.ts index 97d7c73035e6..7c88b4885b84 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.test.ts @@ -92,10 +92,14 @@ describe('Agent actions', () => { await cancelAgentAction(esClient, 'action1'); expect(mockedBulkUpdateAgents).toBeCalled(); - expect(mockedBulkUpdateAgents).toBeCalledWith(expect.anything(), [ - expect.objectContaining({ agentId: 'agent1' }), - expect.objectContaining({ agentId: 'agent2' }), - ]); + expect(mockedBulkUpdateAgents).toBeCalledWith( + expect.anything(), + [ + expect.objectContaining({ agentId: 'agent1' }), + expect.objectContaining({ agentId: 'agent2' }), + ], + {} + ); }); }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index 17c745bfd285..8f9302bd31ac 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -8,6 +8,7 @@ import uuid from 'uuid'; import type { ElasticsearchClient } from '@kbn/core/server'; +import { appContextService } from '../app_context'; import type { Agent, AgentAction, @@ -101,6 +102,32 @@ export async function bulkCreateAgentActions( return actions; } +export async function createErrorActionResults( + esClient: ElasticsearchClient, + actionId: string, + errors: Record, + errorMessage: string +) { + const errorCount = Object.keys(errors).length; + if (errorCount > 0) { + appContextService + .getLogger() + .info( + `Writing error action results of ${errorCount} agents. Possibly failed validation: ${errorMessage}.` + ); + + // writing out error result for those agents that have errors, so the action is not going to stay in progress forever + await bulkCreateAgentActionResults( + esClient, + Object.keys(errors).map((agentId) => ({ + agentId, + actionId, + error: errors[agentId].message, + })) + ); + } +} + export async function bulkCreateAgentActionResults( esClient: ElasticsearchClient, results: Array<{ @@ -227,16 +254,6 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: if (!hit._source || !hit._source.agents || !hit._source.action_id) { continue; } - await createAgentAction(esClient, { - id: cancelActionId, - type: 'CANCEL', - agents: hit._source.agents, - data: { - target_id: hit._source.action_id, - }, - created_at: now, - expiration: hit._source.expiration, - }); if (hit._source.type === 'UPGRADE') { await bulkUpdateAgents( esClient, @@ -246,9 +263,20 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: upgraded_at: null, upgrade_started_at: null, }, - })) + })), + {} ); } + await createAgentAction(esClient, { + id: cancelActionId, + type: 'CANCEL', + agents: hit._source.agents, + data: { + target_id: hit._source.action_id, + }, + created_at: now, + expiration: hit._source.expiration, + }); } return { diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index 55a244664238..d62bbf4c414b 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -11,7 +11,7 @@ import type { SavedObjectsClientContract, ElasticsearchClient } from '@kbn/core/ import type { KueryNode } from '@kbn/es-query'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import type { AgentSOAttributes, Agent, BulkActionResult, ListWithKuery } from '../../types'; +import type { AgentSOAttributes, Agent, ListWithKuery } from '../../types'; import { appContextService, agentPolicyService } from '..'; import type { FleetServerAgent } from '../../../common/types'; import { SO_SEARCH_LIMIT } from '../../../common/constants'; @@ -395,10 +395,11 @@ export async function bulkUpdateAgents( updateData: Array<{ agentId: string; data: Partial; - }> -): Promise<{ items: BulkActionResult[] }> { + }>, + errors: { [key: string]: Error } +): Promise { if (updateData.length === 0) { - return { items: [] }; + return; } const body = updateData.flatMap(({ agentId, data }) => [ @@ -419,14 +420,12 @@ export async function bulkUpdateAgents( refresh: 'wait_for', }); - return { - items: res.items.map((item) => ({ - id: item.update!._id as string, - success: !item.update!.error, + res.items + .filter((item) => item.update!.error) + .forEach((item) => { // @ts-expect-error it not assignable to ErrorCause - error: item.update!.error as Error, - })), - }; + errors[item.update!._id as string] = item.update!.error as Error; + }); } export async function deleteAgent(esClient: ElasticsearchClient, agentId: string) { diff --git a/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts index 96405e464b35..c15857bb4ae3 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign_action_runner.ts @@ -16,7 +16,7 @@ import { appContextService } from '../app_context'; import { ActionRunner } from './action_runner'; import { bulkUpdateAgents } from './crud'; -import { bulkCreateAgentActionResults, createAgentAction } from './actions'; +import { createErrorActionResults, createAgentAction } from './actions'; import { getHostedPolicies, isHostedAgent } from './hosted_agent'; import { BulkActionTaskType } from './bulk_actions_resolver'; @@ -72,7 +72,7 @@ export async function reassignBatch( throw new AgentReassignmentError('No agents to reassign, already assigned or hosted agents'); } - const res = await bulkUpdateAgents( + await bulkUpdateAgents( esClient, agentsToUpdate.map((agent) => ({ agentId: agent.id, @@ -80,18 +80,12 @@ export async function reassignBatch( policy_id: options.newAgentPolicyId, policy_revision: null, }, - })) + })), + errors ); - res.items - .filter((item) => !item.success) - .forEach((item) => { - errors[item.id] = item.error!; - }); - const actionId = options.actionId ?? uuid(); - const errorCount = Object.keys(errors).length; - const total = options.total ?? agentsToUpdate.length + errorCount; + const total = options.total ?? givenAgents.length; const now = new Date().toISOString(); await createAgentAction(esClient, { @@ -105,23 +99,12 @@ export async function reassignBatch( }, }); - if (errorCount > 0) { - appContextService - .getLogger() - .info( - `Skipping ${errorCount} agents, as failed validation (already assigned or assigned to hosted policy)` - ); - - // writing out error result for those agents that failed validation, so the action is not going to stay in progress forever - await bulkCreateAgentActionResults( - esClient, - Object.keys(errors).map((agentId) => ({ - agentId, - actionId, - error: errors[agentId].message, - })) - ); - } + await createErrorActionResults( + esClient, + actionId, + errors, + 'already assigned or assigned to hosted policy' + ); return { actionId }; } diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts index 79612b0bcbf0..5beb5c0a9ac0 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts @@ -115,7 +115,7 @@ describe('unenrollAgents (plural)', () => { // calls ES update with correct values const onlyRegular = [agentInRegularDoc._id, agentInRegularDoc2._id]; - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -128,7 +128,7 @@ describe('unenrollAgents (plural)', () => { } // hosted policy is updated in action results with error - const calledWithActionResults = esClient.bulk.mock.calls[0][0] as estypes.BulkRequest; + const calledWithActionResults = esClient.bulk.mock.calls[1][0] as estypes.BulkRequest; // bulk write two line per create expect(calledWithActionResults.body?.length).toBe(2); const expectedObject = expect.objectContaining({ @@ -170,7 +170,7 @@ describe('unenrollAgents (plural)', () => { }); expect(esClient.bulk.mock.calls.length).toEqual(3); - const bulkBody = (esClient.bulk.mock.calls[1][0] as estypes.BulkRequest)?.body?.[1] as any; + const bulkBody = (esClient.bulk.mock.calls[2][0] as estypes.BulkRequest)?.body?.[1] as any; expect(bulkBody.agent_id).toEqual(agentInRegularDoc._id); expect(bulkBody.action_id).toEqual('other-action'); }); @@ -227,7 +227,7 @@ describe('unenrollAgents (plural)', () => { // calls ES update with correct values const onlyRegular = [agentInRegularDoc._id, agentInRegularDoc2._id]; - const calledWith = esClient.bulk.mock.calls[2][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -239,13 +239,13 @@ describe('unenrollAgents (plural)', () => { expect(doc).toHaveProperty('unenrolled_at'); } - const errorResults = esClient.bulk.mock.calls[1][0]; + const errorResults = esClient.bulk.mock.calls[2][0]; const errorIds = (errorResults as estypes.BulkRequest)?.body ?.filter((i: any) => i.agent_id) .map((i: any) => i.agent_id); expect(errorIds).toEqual([agentInHostedDoc._id]); - const actionResults = esClient.bulk.mock.calls[0][0]; + const actionResults = esClient.bulk.mock.calls[1][0]; const resultIds = (actionResults as estypes.BulkRequest)?.body ?.filter((i: any) => i.agent_id) .map((i: any) => i.agent_id); @@ -290,7 +290,7 @@ describe('unenrollAgents (plural)', () => { expect(unenrolledResponse.actionId).toBeDefined(); // calls ES update with correct values - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -302,7 +302,7 @@ describe('unenrollAgents (plural)', () => { expect(doc).toHaveProperty('unenrolled_at'); } - const actionResults = esClient.bulk.mock.calls[0][0]; + const actionResults = esClient.bulk.mock.calls[1][0]; const resultIds = (actionResults as estypes.BulkRequest)?.body ?.filter((i: any) => i.agent_id) .map((i: any) => i.agent_id); diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts index dd5b4e023c2a..c735254f1825 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll_action_runner.ts @@ -25,6 +25,7 @@ import { bulkUpdateAgents } from './crud'; import { bulkCreateAgentActionResults, createAgentAction, + createErrorActionResults, getUnenrollAgentActions, } from './actions'; import { getHostedPolicies, isHostedAgent } from './hosted_agent'; @@ -81,13 +82,24 @@ export async function unenrollBatch( return agents; }, []); + const now = new Date().toISOString(); + + // Update the necessary agents + const updateData = options.revoke + ? { unenrolled_at: now, active: false } + : { unenrollment_started_at: now }; + + await bulkUpdateAgents( + esClient, + agentsToUpdate.map(({ id }) => ({ agentId: id, data: updateData })), + outgoingErrors + ); + const actionId = options.actionId ?? uuid(); - const errorCount = Object.keys(outgoingErrors).length; const total = options.total ?? givenAgents.length; const agentIds = agentsToUpdate.map((agent) => agent.id); - const now = new Date().toISOString(); if (options.revoke) { // Get all API keys that need to be invalidated await invalidateAPIKeysForAgents(agentsToUpdate); @@ -104,32 +116,11 @@ export async function unenrollBatch( }); } - if (errorCount > 0) { - appContextService - .getLogger() - .info( - `Skipping ${errorCount} agents, as failed validation (cannot unenroll from a hosted policy or already unenrolled)` - ); - - // writing out error result for those agents that failed validation, so the action is not going to stay in progress forever - await bulkCreateAgentActionResults( - esClient, - Object.keys(outgoingErrors).map((agentId) => ({ - agentId, - actionId, - error: outgoingErrors[agentId].message, - })) - ); - } - - // Update the necessary agents - const updateData = options.revoke - ? { unenrolled_at: now, active: false } - : { unenrollment_started_at: now }; - - await bulkUpdateAgents( + await createErrorActionResults( esClient, - agentsToUpdate.map(({ id }) => ({ agentId: id, data: updateData })) + actionId, + outgoingErrors, + 'cannot unenroll from a hosted policy or already unenrolled' ); return { diff --git a/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts index 48f7b455d36b..11d42bc76e39 100644 --- a/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts @@ -11,14 +11,16 @@ import { difference, uniq } from 'lodash'; import type { Agent } from '../../types'; -import { appContextService } from '../app_context'; - import { ActionRunner } from './action_runner'; import { bulkUpdateAgents } from './crud'; import { BulkActionTaskType } from './bulk_actions_resolver'; import { filterHostedPolicies } from './filter_hosted_agents'; -import { bulkCreateAgentActionResults, createAgentAction } from './actions'; +import { + createErrorActionResults, + bulkCreateAgentActionResults, + createAgentAction, +} from './actions'; export class UpdateAgentTagsActionRunner extends ActionRunner { protected async processAgents(agents: Agent[]): Promise<{ actionId: string }> { @@ -90,12 +92,12 @@ export async function updateTagsBatch( data: { tags: getNewTags(agent), }, - })) + })), + errors ); const actionId = options.actionId ?? uuid(); const total = options.total ?? givenAgents.length; - const errorCount = Object.keys(errors).length; // creating an action doc so that update tags shows up in activity await createAgentAction(esClient, { @@ -113,23 +115,12 @@ export async function updateTagsBatch( })) ); - if (errorCount > 0) { - appContextService - .getLogger() - .info( - `Skipping ${errorCount} agents, as failed validation (cannot modified tags on hosted agents)` - ); - - // writing out error result for those agents that failed validation, so the action is not going to stay in progress forever - await bulkCreateAgentActionResults( - esClient, - Object.keys(errors).map((agentId) => ({ - agentId, - actionId, - error: errors[agentId].message, - })) - ); - } + await createErrorActionResults( + esClient, + actionId, + errors, + 'cannot modified tags on hosted agents' + ); return { actionId }; } diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts index db880f56ef47..8b888126ce11 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts @@ -50,7 +50,7 @@ describe('sendUpgradeAgentsActions (plural)', () => { // calls ES update with correct values const onlyRegular = [agentInRegularDoc._id, agentInRegularDoc2._id]; - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -64,7 +64,7 @@ describe('sendUpgradeAgentsActions (plural)', () => { } // hosted policy is updated in action results with error - const calledWithActionResults = esClient.bulk.mock.calls[0][0] as estypes.BulkRequest; + const calledWithActionResults = esClient.bulk.mock.calls[1][0] as estypes.BulkRequest; // bulk write two line per create expect(calledWithActionResults.body?.length).toBe(2); const expectedObject = expect.objectContaining({ diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts index a34f189871a3..c6794de6e2dc 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts @@ -22,7 +22,7 @@ import { ActionRunner } from './action_runner'; import type { GetAgentsOptions } from './crud'; import { bulkUpdateAgents } from './crud'; -import { bulkCreateAgentActionResults, createAgentAction } from './actions'; +import { createErrorActionResults, createAgentAction } from './actions'; import { getHostedPolicies, isHostedAgent } from './hosted_agent'; import { BulkActionTaskType } from './bulk_actions_resolver'; @@ -108,9 +108,20 @@ export async function upgradeBatch( options.upgradeDurationSeconds ); + await bulkUpdateAgents( + esClient, + agentsToUpdate.map((agent) => ({ + agentId: agent.id, + data: { + upgraded_at: null, + upgrade_started_at: now, + }, + })), + errors + ); + const actionId = options.actionId ?? uuid(); - const errorCount = Object.keys(errors).length; - const total = options.total ?? agentsToUpdate.length + errorCount; + const total = options.total ?? givenAgents.length; await createAgentAction(esClient, { id: actionId, @@ -123,33 +134,11 @@ export async function upgradeBatch( ...rollingUpgradeOptions, }); - if (errorCount > 0) { - appContextService - .getLogger() - .info( - `Skipping ${errorCount} agents, as failed validation (cannot upgrade hosted agent or agent not upgradeable)` - ); - - // writing out error result for those agents that failed validation, so the action is not going to stay in progress forever - await bulkCreateAgentActionResults( - esClient, - Object.keys(errors).map((agentId) => ({ - agentId, - actionId, - error: errors[agentId].message, - })) - ); - } - - await bulkUpdateAgents( + await createErrorActionResults( esClient, - agentsToUpdate.map((agent) => ({ - agentId: agent.id, - data: { - upgraded_at: null, - upgrade_started_at: now, - }, - })) + actionId, + errors, + 'cannot upgrade hosted agent or agent not upgradeable' ); return { From 5203f1bcc86e777656fa7e7f0036638d83883f51 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 3 Oct 2022 10:25:39 +0200 Subject: [PATCH 183/185] [ML] Explain Log Rate Spikes: Fix loading state for grouping. (#141770) Fix loading state messages for grouping: - Fixes the progress bar messages to include the grouping step. - If progress stalls, it might look to the user like the analysis got stuck when there are steps like the grouping that take a longer time. This updates the progress bar to show an animated background as long as the analysis is running. When the analysis finishes or gets canceled the animated background gets disabled and shows a static background. --- .../packages/ml/aiops_components/BUILD.bazel | 4 ++ .../progress_controls/progress_controls.tsx | 12 ++++- .../use_animated_progress_bar_background.ts | 53 +++++++++++++++++++ .../ml/aiops_components/tsconfig.json | 3 +- .../server/routes/explain_log_rate_spikes.ts | 53 ++++++++++++++----- 5 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 x-pack/packages/ml/aiops_components/src/progress_controls/use_animated_progress_bar_background.ts diff --git a/x-pack/packages/ml/aiops_components/BUILD.bazel b/x-pack/packages/ml/aiops_components/BUILD.bazel index 37ed6c171c4a..08b49643adc2 100644 --- a/x-pack/packages/ml/aiops_components/BUILD.bazel +++ b/x-pack/packages/ml/aiops_components/BUILD.bazel @@ -55,6 +55,8 @@ RUNTIME_DEPS = [ "@npm//react", "@npm//@elastic/charts", "@npm//@elastic/eui", + "@npm//@emotion/react", + "@npm//@emotion/css", "//packages/kbn-i18n-react", "//x-pack/packages/ml/aiops_utils", ] @@ -78,6 +80,8 @@ TYPES_DEPS = [ "@npm//@types/react", "@npm//@elastic/charts", "@npm//@elastic/eui", + "@npm//@emotion/react", + "@npm//@emotion/css", "//packages/kbn-i18n-react:npm_module_types", "//x-pack/packages/ml/aiops_utils:npm_module_types", ] diff --git a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx index 579c528d16dc..af8f0ec1ad31 100644 --- a/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx +++ b/x-pack/packages/ml/aiops_components/src/progress_controls/progress_controls.tsx @@ -5,7 +5,10 @@ * 2.0. */ +import React from 'react'; + import { + useEuiTheme, EuiButton, EuiFlexGroup, EuiFlexItem, @@ -13,9 +16,11 @@ import { EuiProgress, EuiText, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React from 'react'; + +import { useAnimatedProgressBarBackground } from './use_animated_progress_bar_background'; // TODO Consolidate with duplicate component `CorrelationsProgressControls` in // `x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx` @@ -37,6 +42,9 @@ export function ProgressControls({ isRunning, shouldRerunAnalysis, }: ProgressControlProps) { + const { euiTheme } = useEuiTheme(); + const runningProgressBarStyles = useAnimatedProgressBarBackground(euiTheme.colors.success); + return ( @@ -51,7 +59,7 @@ export function ProgressControls({ /> - + { + return useMemo(() => { + const progressBackground = { + background: `repeating-linear-gradient( + -45deg, + transparent 0 6px, + rgba(0, 0, 0, 0.1) 6px 12px + ), + ${color}`, + // 0.707 = cos(45deg) + backgroundSize: 'calc(12px / 0.707) 100%, 100% 800%', + backgroundPosition: 'inherit', + }; + + return css({ + 'progress[value]': { + animation: 'aiopsAnimatedProgress 4s infinite linear', + + '::-webkit-progress-inner-element': { + overflow: 'hidden', + backgroundPosition: 'inherit', + }, + '::-webkit-progress-bar': { + backgroundColor: 'transparent', + backgroundPosition: 'inherit', + }, + + '::-webkit-progress-value': progressBackground, + '::-moz-progress-bar': progressBackground, + + '@keyframes aiopsAnimatedProgress': { + '0%': { + backgroundPosition: '0 0', + }, + '100%': { + backgroundPosition: 'calc(10 * (12px / 0.707)) 100%', + }, + }, + }, + }); + }, [color]); +}; diff --git a/x-pack/packages/ml/aiops_components/tsconfig.json b/x-pack/packages/ml/aiops_components/tsconfig.json index 8bca748268ac..cdb1c5d8d000 100644 --- a/x-pack/packages/ml/aiops_components/tsconfig.json +++ b/x-pack/packages/ml/aiops_components/tsconfig.json @@ -12,7 +12,8 @@ "@types/d3-transition", "jest", "node", - "react" + "react", + "@emotion/react/types/css-prop" ] }, "include": [ diff --git a/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts index 48ea2dbddb1c..8a8a7372f80e 100644 --- a/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts +++ b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts @@ -52,8 +52,10 @@ import { // Overall progress is a float from 0 to 1. const LOADED_FIELD_CANDIDATES = 0.2; -const PROGRESS_STEP_P_VALUES = 0.6; -const PROGRESS_STEP_HISTOGRAMS = 0.2; +const PROGRESS_STEP_P_VALUES = 0.5; +const PROGRESS_STEP_GROUPING = 0.1; +const PROGRESS_STEP_HISTOGRAMS = 0.1; +const PROGRESS_STEP_HISTOGRAMS_GROUPS = 0.1; export const defineExplainLogRateSpikesRoute = ( router: IRouter, @@ -233,7 +235,35 @@ export const defineExplainLogRateSpikesRoute = ( undefined )) as [NumericChartData]; + function pushHistogramDataLoadingState() { + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.explainLogRateSpikes.loadingState.loadingHistogramData', + { + defaultMessage: 'Loading histogram data.', + } + ), + }) + ); + } + if (groupingEnabled) { + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.explainLogRateSpikes.loadingState.groupingResults', + { + defaultMessage: 'Transforming significant field/value pairs into groups.', + } + ), + }) + ); + // To optimize the `frequent_items` query, we identify duplicate change points by count attributes. // Note this is a compromise and not 100% accurate because there could be change points that // have the exact same counts but still don't co-occur. @@ -389,6 +419,10 @@ export const defineExplainLogRateSpikesRoute = ( push(addChangePointsGroupAction(changePointGroups)); } + loaded += PROGRESS_STEP_GROUPING; + + pushHistogramDataLoadingState(); + if (changePointGroups) { await asyncForEach(changePointGroups, async (cpg, index) => { const histogramQuery = { @@ -445,6 +479,8 @@ export const defineExplainLogRateSpikesRoute = ( } } + loaded += PROGRESS_STEP_HISTOGRAMS_GROUPS; + // time series filtered by fields if (changePoints) { await asyncForEach(changePoints, async (cp, index) => { @@ -496,18 +532,7 @@ export const defineExplainLogRateSpikesRoute = ( const { fieldName, fieldValue } = cp; loaded += (1 / changePoints.length) * PROGRESS_STEP_HISTOGRAMS; - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.explainLogRateSpikes.loadingState.loadingHistogramData', - { - defaultMessage: 'Loading histogram data.', - } - ), - }) - ); + pushHistogramDataLoadingState(); push( addChangePointsHistogramAction([ { From 656a48fee9cd6e0f013b830187d94ea5b964cf91 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 3 Oct 2022 09:30:06 +0100 Subject: [PATCH 184/185] [ML] Api tests for ml/modules/jobs_exist/{moduleId} (#142378) * [ML] Api tests for ml/modules/jobs_exist/{moduleId} * fixing title * fixing delete index pattern function --- .../api_integration/apis/ml/modules/index.ts | 1 + .../apis/ml/modules/jobs_exist.ts | 119 ++++++++++++++++++ x-pack/test/functional/services/ml/api.ts | 18 +++ .../functional/services/ml/test_resources.ts | 88 ++++++++----- 4 files changed, 197 insertions(+), 29 deletions(-) create mode 100644 x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts index de8919976fd3..d28263b48770 100644 --- a/x-pack/test/api_integration/apis/ml/modules/index.ts +++ b/x-pack/test/api_integration/apis/ml/modules/index.ts @@ -38,5 +38,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get_module')); loadTestFile(require.resolve('./recognize_module')); loadTestFile(require.resolve('./setup_module')); + loadTestFile(require.resolve('./jobs_exist')); }); } diff --git a/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts b/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts new file mode 100644 index 000000000000..3e5cd0aa996d --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const idSpace1 = 'space1'; + const sourceDataArchive = 'x-pack/test/functional/es_archives/ml/module_sample_logs'; + const moduleInfo = { + moduleId: 'sample_data_weblogs', + jobIds: ['low_request_rate', 'response_code_rates', 'url_scanning'], + dataView: { name: 'ft_module_sample_logs', timeField: '@timestamp' }, + }; + + async function runRequest(moduleId: string, expectedStatusCode: number, user: USER) { + const { body, status } = await supertest + .get(`/api/ml/modules/jobs_exist/${moduleId}`) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_REQUEST_HEADERS); + + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + return body; + } + + describe('GET ml/modules/jobs_exist/{moduleId}', function () { + before(async () => { + await ml.testResources.setKibanaTimeZoneToUTC(); + await esArchiver.loadIfNeeded(sourceDataArchive); + // create data view in default space + await ml.testResources.createIndexPatternIfNeeded( + moduleInfo.dataView.name, + moduleInfo.dataView.timeField + ); + // create data view in idSpace1 + await ml.testResources.createIndexPatternIfNeeded( + moduleInfo.dataView.name, + moduleInfo.dataView.timeField, + idSpace1 + ); + }); + + afterEach(async () => { + await ml.api.cleanMlIndices(); + }); + + after(async () => { + // delete all data views in all spaces + await ml.testResources.deleteIndexPatternByTitle(moduleInfo.dataView.name); + await ml.testResources.deleteIndexPatternByTitle(moduleInfo.dataView.name, idSpace1); + }); + + it('should find jobs installed by module without prefix', async () => { + const prefix = ''; + await ml.api.setupModule(moduleInfo.moduleId, { + prefix, + indexPatternName: moduleInfo.dataView.name, + startDatafeed: false, + estimateModelMemory: false, + }); + const { jobsExist, jobs } = await runRequest(moduleInfo.moduleId, 200, USER.ML_POWERUSER); + + const expectedJobIds = moduleInfo.jobIds.map((j) => ({ id: `${prefix}${j}` })); + expect(jobsExist).to.eql(true, 'Expected jobsExist to be true'); + expect(jobs).to.eql(expectedJobIds, `Expected jobs to be ${expectedJobIds}`); + }); + + it('should find jobs installed by module with prefix', async () => { + const prefix = 'pf1_'; + await ml.api.setupModule(moduleInfo.moduleId, { + prefix, + indexPatternName: moduleInfo.dataView.name, + startDatafeed: false, + estimateModelMemory: false, + }); + const { jobsExist, jobs } = await runRequest(moduleInfo.moduleId, 200, USER.ML_POWERUSER); + + const expectedJobIds = moduleInfo.jobIds.map((j) => ({ id: `${prefix}${j}` })); + expect(jobsExist).to.eql(true, 'Expected jobsExist to be true'); + expect(jobs).to.eql(expectedJobIds, `Expected jobs to be ${expectedJobIds}`); + }); + + it('should not find jobs installed into a different space', async () => { + const prefix = 'pf1_'; + await ml.api.setupModule( + moduleInfo.moduleId, + { + prefix, + indexPatternName: moduleInfo.dataView.name, + startDatafeed: false, + estimateModelMemory: false, + }, + idSpace1 + ); + const { jobsExist, jobs } = await runRequest(moduleInfo.moduleId, 200, USER.ML_POWERUSER); + + expect(jobsExist).to.eql(false, 'Expected jobsExist to be false'); + expect(jobs).to.eql(undefined, `Expected jobs to be undefined`); + }); + + it("should not find jobs for module which hasn't been installed", async () => { + const { jobsExist, jobs } = await runRequest('apache_ecs', 200, USER.ML_POWERUSER); + + expect(jobsExist).to.eql(false, 'Expected jobsExist to be false'); + expect(jobs).to.eql(undefined, `Expected jobs to be undefined`); + }); + }); +}; diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 62d46e644f17..d6802d2cb0f7 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -8,6 +8,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import expect from '@kbn/expect'; import { ProvidedType } from '@kbn/test'; +import type { TypeOf } from '@kbn/config-schema'; import fs from 'fs'; import { Calendar } from '@kbn/ml-plugin/server/models/calendar'; import { Annotation } from '@kbn/ml-plugin/common/types/annotations'; @@ -17,6 +18,7 @@ import { DataFrameTaskStateType } from '@kbn/ml-plugin/common/types/data_frame_a import { DATA_FRAME_TASK_STATE } from '@kbn/ml-plugin/common/constants/data_frame_analytics'; import { Datafeed, Job } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; import { JobType } from '@kbn/ml-plugin/common/types/saved_objects'; +import { setupModuleBodySchema } from '@kbn/ml-plugin/server/routes/schemas/modules'; import { ML_ANNOTATIONS_INDEX_ALIAS_READ, ML_ANNOTATIONS_INDEX_ALIAS_WRITE, @@ -1445,5 +1447,21 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { log.debug('> Ingest pipeline deleted'); }, + + async setupModule( + moduleId: string, + body: TypeOf, + space?: string + ) { + log.debug(`Setting up module with ID: "${moduleId}"`); + const { body: module, status } = await kbnSupertest + .post(`${space ? `/s/${space}` : ''}/api/ml/modules/setup/${moduleId}`) + .set(COMMON_REQUEST_HEADERS) + .send(body); + this.assertResponseStatusCode(200, status, module); + + log.debug('Module set up'); + return module; + }, }; } diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts index d1a7557caf2b..56d01a4f1a86 100644 --- a/x-pack/test/functional/services/ml/test_resources.ts +++ b/x-pack/test/functional/services/ml/test_resources.ts @@ -54,15 +54,25 @@ export function MachineLearningTestResourcesProvider( await kibanaServer.uiSettings.unset('hideAnnouncements'); }, - async savedObjectExistsById(id: string, objectType: SavedObjectType): Promise { - const response = await supertest.get(`/api/saved_objects/${objectType}/${id}`); + async savedObjectExistsById( + id: string, + objectType: SavedObjectType, + space?: string + ): Promise { + const response = await supertest.get( + `${space ? `/s/${space}` : ''}/api/saved_objects/${objectType}/${id}` + ); return response.status === 200; }, - async savedObjectExistsByTitle(title: string, objectType: SavedObjectType): Promise { - const id = await this.getSavedObjectIdByTitle(title, objectType); + async savedObjectExistsByTitle( + title: string, + objectType: SavedObjectType, + space?: string + ): Promise { + const id = await this.getSavedObjectIdByTitle(title, objectType, space); if (id) { - return await this.savedObjectExistsById(id, objectType); + return await this.savedObjectExistsById(id, objectType, space); } else { return false; } @@ -70,11 +80,14 @@ export function MachineLearningTestResourcesProvider( async getSavedObjectIdByTitle( title: string, - objectType: SavedObjectType + objectType: SavedObjectType, + space?: string ): Promise { log.debug(`Searching for '${objectType}' with title '${title}'...`); const { body: findResponse, status } = await supertest - .get(`/api/saved_objects/_find?type=${objectType}&per_page=10000`) + .get( + `${space ? `/s/${space}` : ''}/api/saved_objects/_find?type=${objectType}&per_page=10000` + ) .set(COMMON_REQUEST_HEADERS); mlApi.assertResponseStatusCode(200, status, findResponse); @@ -104,8 +117,8 @@ export function MachineLearningTestResourcesProvider( return savedObjectIds; }, - async getIndexPatternId(title: string): Promise { - return this.getSavedObjectIdByTitle(title, SavedObjectType.INDEX_PATTERN); + async getIndexPatternId(title: string, space?: string): Promise { + return this.getSavedObjectIdByTitle(title, SavedObjectType.INDEX_PATTERN, space); }, async getSavedSearchId(title: string): Promise { @@ -120,7 +133,11 @@ export function MachineLearningTestResourcesProvider( return this.getSavedObjectIdByTitle(title, SavedObjectType.DASHBOARD); }, - async createIndexPattern(title: string, timeFieldName?: string): Promise { + async createIndexPattern( + title: string, + timeFieldName?: string, + space?: string + ): Promise { log.debug( `Creating index pattern with title '${title}'${ timeFieldName !== undefined ? ` and time field '${timeFieldName}'` : '' @@ -128,12 +145,12 @@ export function MachineLearningTestResourcesProvider( ); const { body: createResponse, status } = await supertest - .post(`/api/saved_objects/${SavedObjectType.INDEX_PATTERN}`) + .post(`${space ? `/s/${space}` : ''}/api/saved_objects/${SavedObjectType.INDEX_PATTERN}`) .set(COMMON_REQUEST_HEADERS) .send({ attributes: { title, timeFieldName } }); mlApi.assertResponseStatusCode(200, status, createResponse); - await this.assertIndexPatternExistByTitle(title); + await this.assertIndexPatternExistByTitle(title, space); log.debug(` > Created with id '${createResponse.id}'`); return createResponse.id; @@ -152,13 +169,17 @@ export function MachineLearningTestResourcesProvider( return createResponse; }, - async createIndexPatternIfNeeded(title: string, timeFieldName?: string): Promise { - const indexPatternId = await this.getIndexPatternId(title); + async createIndexPatternIfNeeded( + title: string, + timeFieldName?: string, + space?: string + ): Promise { + const indexPatternId = await this.getIndexPatternId(title, space); if (indexPatternId !== undefined) { log.debug(`Index pattern with title '${title}' already exists. Nothing to create.`); return indexPatternId; } else { - return await this.createIndexPattern(title, timeFieldName); + return await this.createIndexPattern(title, timeFieldName, space); } }, @@ -301,7 +322,12 @@ export function MachineLearningTestResourcesProvider( ); }, - async deleteSavedObjectById(id: string, objectType: SavedObjectType, force: boolean = false) { + async deleteSavedObjectById( + id: string, + objectType: SavedObjectType, + force: boolean = false, + space?: string + ) { log.debug(`Deleting ${objectType} with id '${id}'...`); if ((await this.savedObjectExistsById(id, objectType)) === false) { @@ -309,31 +335,31 @@ export function MachineLearningTestResourcesProvider( return; } else { const { body, status } = await supertest - .delete(`/api/saved_objects/${objectType}/${id}`) + .delete(`${space ? `/s/${space}` : ''}/api/saved_objects/${objectType}/${id}`) .set(COMMON_REQUEST_HEADERS) .query({ force }); mlApi.assertResponseStatusCode(200, status, body); - await this.assertSavedObjectNotExistsById(id, objectType); + await this.assertSavedObjectNotExistsById(id, objectType, space); log.debug(` > Deleted ${objectType} with id '${id}'`); } }, - async deleteIndexPatternByTitle(title: string) { + async deleteIndexPatternByTitle(title: string, space?: string) { log.debug(`Deleting index pattern with title '${title}'...`); - const indexPatternId = await this.getIndexPatternId(title); + const indexPatternId = await this.getIndexPatternId(title, space); if (indexPatternId === undefined) { log.debug(`Index pattern with title '${title}' does not exists. Nothing to delete.`); return; } else { - await this.deleteIndexPatternById(indexPatternId); + await this.deleteIndexPatternById(indexPatternId, space); } }, - async deleteIndexPatternById(id: string) { - await this.deleteSavedObjectById(id, SavedObjectType.INDEX_PATTERN); + async deleteIndexPatternById(id: string, space?: string) { + await this.deleteSavedObjectById(id, SavedObjectType.INDEX_PATTERN, false, space); }, async deleteSavedSearchByTitle(title: string) { @@ -396,12 +422,16 @@ export function MachineLearningTestResourcesProvider( } }, - async assertSavedObjectExistsByTitle(title: string, objectType: SavedObjectType) { + async assertSavedObjectExistsByTitle( + title: string, + objectType: SavedObjectType, + space?: string + ) { await retry.waitForWithTimeout( `${objectType} with title '${title}' to exist`, 5 * 1000, async () => { - if ((await this.savedObjectExistsByTitle(title, objectType)) === true) { + if ((await this.savedObjectExistsByTitle(title, objectType, space)) === true) { return true; } else { throw new Error(`${objectType} with title '${title}' should exist.`); @@ -438,12 +468,12 @@ export function MachineLearningTestResourcesProvider( ); }, - async assertSavedObjectNotExistsById(id: string, objectType: SavedObjectType) { + async assertSavedObjectNotExistsById(id: string, objectType: SavedObjectType, space?: string) { await retry.waitForWithTimeout( `${objectType} with id '${id}' not to exist`, 5 * 1000, async () => { - if ((await this.savedObjectExistsById(id, objectType)) === false) { + if ((await this.savedObjectExistsById(id, objectType, space)) === false) { return true; } else { throw new Error(`${objectType} with id '${id}' should not exist.`); @@ -452,8 +482,8 @@ export function MachineLearningTestResourcesProvider( ); }, - async assertIndexPatternExistByTitle(title: string) { - await this.assertSavedObjectExistsByTitle(title, SavedObjectType.INDEX_PATTERN); + async assertIndexPatternExistByTitle(title: string, space?: string) { + await this.assertSavedObjectExistsByTitle(title, SavedObjectType.INDEX_PATTERN, space); }, async assertIndexPatternExistById(id: string) { From 3b07dcdd4bf6ffd067209b7baf2d2f5776891b66 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 3 Oct 2022 12:54:40 +0200 Subject: [PATCH 185/185] [ML] Add a tooltip for the Avg inference time column (#142355) * add a tooltip for avg inference time * update message --- .../nodes_overview/allocated_models.tsx | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx index d1808c2c8213..198c01806e06 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx @@ -5,8 +5,17 @@ * 2.0. */ import React, { FC } from 'react'; -import { EuiBadge, EuiIcon, EuiInMemoryTable, EuiToolTip } from '@elastic/eui'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiInMemoryTable, + EuiToolTip, + useEuiTheme, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import type { @@ -27,6 +36,7 @@ export const AllocatedModels: FC = ({ const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); const durationFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DURATION); + const euiTheme = useEuiTheme(); const columns: Array> = [ { @@ -97,11 +107,36 @@ export const AllocatedModels: FC = ({ 'data-test-subj': 'mlAllocatedModelsTableThroughput', }, { - name: i18n.translate( - 'xpack.ml.trainedModels.nodesList.modelsList.modelAvgInferenceTimeHeader', - { - defaultMessage: 'Avg inference time', - } + name: ( + + } + content={ + + } + > + + + + + + + + + + + ), width: '100px', truncateText: false,

    rk!lwQj$yx+= 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