From d3b44c41cd7c5f8ba19f96d1cfb83c3992866a87 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Mon, 20 Sep 2021 13:19:18 -0400 Subject: [PATCH 01/36] [Fleet] Support automatic upgrades of Package Policies when updating Integrations (#108269) * Add initial upgrade policy modal * Fix modal bolding * Fetch dry run data on settings page * Fix request for agent count data * Add conflict CallOut to modal + call upgrade endpoint on submit * Add conflict detection for type mismatched variables * Clean up upgrade logic + attempt to fix modal closing bug * Hoist state for policy upgrade piece of modal logic * Fix update icon flex style * Add unused test package fixture * Fix 0.3.0 test fixture * Reset test 0.3.0 test fixture to master * Remove unused suppressToasts param * Address low hanging PR feedback * First pass at refactoring InstallationButton component per PR review * Finalize refactor of InstallationButton component * Fix lint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_package_policy_page/index.tsx | 2 +- .../hooks/use_package_install.tsx | 1 + .../sections/epm/screens/detail/index.tsx | 2 +- .../detail/settings/install_button.tsx | 84 +++++ .../detail/settings/installation_button.tsx | 161 --------- .../epm/screens/detail/settings/settings.tsx | 118 +++++-- .../detail/settings/uninstall_button.tsx | 92 ++++++ .../screens/detail/settings/update_button.tsx | 310 ++++++++++++++++++ .../package_policy_actions_menu.tsx | 5 +- .../public/hooks/use_request/agent_policy.ts | 8 + .../hooks/use_request/package_policy.ts | 19 +- 11 files changed, 604 insertions(+), 198 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index d887076568a6..35092cb67f7e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -726,7 +726,7 @@ const UpgradeStatusCallout: React.FunctionComponent<{ > {packageInfo.version} {updateAvailable ? ( - + ) : null} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx new file mode 100644 index 000000000000..f2813058afe5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx @@ -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 { EuiButton } from '@elastic/eui'; +import React, { Fragment, useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { PackageInfo, UpgradePackagePolicyDryRunResponse } from '../../../../../types'; +import { InstallStatus } from '../../../../../types'; +import { + useCapabilities, + useGetPackageInstallStatus, + useInstallPackage, +} from '../../../../../hooks'; + +import { ConfirmPackageInstall } from './confirm_package_install'; + +type InstallationButtonProps = Pick & { + disabled?: boolean; + dryRunData?: UpgradePackagePolicyDryRunResponse | null; + isUpgradingPackagePolicies?: boolean; + latestVersion?: string; + numOfAssets: number; + packagePolicyIds?: string[]; + setIsUpgradingPackagePolicies?: React.Dispatch>; +}; +export function InstallButton(props: InstallationButtonProps) { + const { name, numOfAssets, title, version } = props; + const hasWriteCapabilites = useCapabilities().write; + const installPackage = useInstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + + const isInstalling = installationStatus === InstallStatus.installing; + const [isInstallModalVisible, setIsInstallModalVisible] = useState(false); + + const toggleInstallModal = useCallback(() => { + setIsInstallModalVisible(!isInstallModalVisible); + }, [isInstallModalVisible]); + + const handleClickInstall = useCallback(() => { + installPackage({ name, version, title }); + toggleInstallModal(); + }, [installPackage, name, title, toggleInstallModal, version]); + + const installModal = ( + + ); + + return hasWriteCapabilites ? ( + + + {isInstalling ? ( + + ) : ( + + )} + + + {isInstallModalVisible && installModal} + + ) : null; +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx deleted file mode 100644 index eab28a051f06..000000000000 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx +++ /dev/null @@ -1,161 +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 { EuiButton } from '@elastic/eui'; -import React, { Fragment, useCallback, useMemo, useState } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import type { PackageInfo } from '../../../../../types'; -import { InstallStatus } from '../../../../../types'; -import { - useCapabilities, - useUninstallPackage, - useGetPackageInstallStatus, - useInstallPackage, -} from '../../../../../hooks'; - -import { ConfirmPackageUninstall } from './confirm_package_uninstall'; -import { ConfirmPackageInstall } from './confirm_package_install'; - -type InstallationButtonProps = Pick & { - disabled?: boolean; - isUpdate?: boolean; - latestVersion?: string; -}; -export function InstallationButton(props: InstallationButtonProps) { - const { assets, name, title, version, disabled = true, isUpdate = false, latestVersion } = props; - const hasWriteCapabilites = useCapabilities().write; - const installPackage = useInstallPackage(); - const uninstallPackage = useUninstallPackage(); - const getPackageInstallStatus = useGetPackageInstallStatus(); - const { status: installationStatus } = getPackageInstallStatus(name); - - const isInstalling = installationStatus === InstallStatus.installing; - const isRemoving = installationStatus === InstallStatus.uninstalling; - const isInstalled = installationStatus === InstallStatus.installed; - const showUninstallButton = isInstalled || isRemoving; - const [isModalVisible, setModalVisible] = useState(false); - const toggleModal = useCallback(() => { - setModalVisible(!isModalVisible); - }, [isModalVisible]); - - const handleClickInstall = useCallback(() => { - installPackage({ name, version, title }); - toggleModal(); - }, [installPackage, name, title, toggleModal, version]); - - const handleClickUpdate = useCallback(() => { - installPackage({ name, version, title, fromUpdate: true }); - }, [installPackage, name, title, version]); - - const handleClickUninstall = useCallback(() => { - uninstallPackage({ name, version, title, redirectToVersion: latestVersion ?? version }); - toggleModal(); - }, [uninstallPackage, name, title, toggleModal, version, latestVersion]); - - // counts the number of assets in the package - const numOfAssets = useMemo( - () => - Object.entries(assets).reduce( - (acc, [serviceName, serviceNameValue]) => - acc + - Object.entries(serviceNameValue).reduce( - (acc2, [assetName, assetNameValue]) => acc2 + assetNameValue.length, - 0 - ), - 0 - ), - [assets] - ); - - const installButton = ( - - {isInstalling ? ( - - ) : ( - - )} - - ); - - const updateButton = ( - - - - ); - - const uninstallButton = ( - - {isRemoving ? ( - - ) : ( - - )} - - ); - - const uninstallModal = ( - - ); - - const installModal = ( - - ); - - return hasWriteCapabilites ? ( - - {isUpdate ? updateButton : showUninstallButton ? uninstallButton : installButton} - {isModalVisible && (isInstalled ? uninstallModal : installModal)} - - ) : null; -} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 98cc172197d4..07c95e0d77ec 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -5,30 +5,42 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import semverLt from 'semver/functions/lt'; -import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer, EuiLink } from '@elastic/eui'; +import { + EuiCallOut, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiSpacer, + EuiLink, +} from '@elastic/eui'; -import type { PackageInfo } from '../../../../../types'; +import { i18n } from '@kbn/i18n'; + +import type { PackageInfo, UpgradePackagePolicyDryRunResponse } from '../../../../../types'; import { InstallStatus } from '../../../../../types'; -import { useGetPackagePolicies, useGetPackageInstallStatus, useLink } from '../../../../../hooks'; +import { + useGetPackagePolicies, + useGetPackageInstallStatus, + useLink, + sendUpgradePackagePolicyDryRun, +} from '../../../../../hooks'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../constants'; -import { UpdateIcon } from '../components'; -import { InstallationButton } from './installation_button'; +import { InstallButton } from './install_button'; +import { UpdateButton } from './update_button'; +import { UninstallButton } from './uninstall_button'; const SettingsTitleCell = styled.td` padding-right: ${(props) => props.theme.eui.spacerSizes.xl}; padding-bottom: ${(props) => props.theme.eui.spacerSizes.m}; `; -const UpdatesAvailableMsgContainer = styled.span` - padding-left: ${(props) => props.theme.eui.spacerSizes.s}; -`; - const NoteLabel = () => ( ( /> ); -const UpdatesAvailableMsg = () => ( - - +const UpdatesAvailableMsg = ({ latestVersion }: { latestVersion: string }) => ( + - + ); const LatestVersionLink = ({ name, version }: { name: string; version: string }) => { @@ -68,14 +86,35 @@ interface Props { export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { const { name, title, removable, latestVersion, version } = packageInfo; + const [dryRunData, setDryRunData] = useState(); + const [isUpgradingPackagePolicies, setIsUpgradingPackagePolicies] = useState(false); const getPackageInstallStatus = useGetPackageInstallStatus(); const { data: packagePoliciesData } = useGetPackagePolicies({ - perPage: 0, + perPage: 1000, page: 1, kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${name}`, }); + const { status: installationStatus, version: installedVersion } = getPackageInstallStatus(name); const packageHasUsages = !!packagePoliciesData?.total; + + const packagePolicyIds = useMemo( + () => packagePoliciesData?.items.map(({ id }) => id), + [packagePoliciesData] + ); + + useEffect(() => { + const fetchDryRunData = async () => { + if (packagePolicyIds && packagePolicyIds.length) { + const { data } = await sendUpgradePackagePolicyDryRun(packagePolicyIds, latestVersion); + + setDryRunData(data); + } + }; + + fetchDryRunData(); + }, [latestVersion, packagePolicyIds]); + const updateAvailable = installedVersion && semverLt(installedVersion, latestVersion) ? true : false; @@ -88,6 +127,20 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { const isUpdating = installationStatus === InstallStatus.installing && installedVersion; + const numOfAssets = useMemo( + () => + Object.entries(packageInfo.assets).reduce( + (acc, [serviceName, serviceNameValue]) => + acc + + Object.entries(serviceNameValue).reduce( + (acc2, [assetName, assetNameValue]) => acc2 + assetNameValue.length, + 0 + ), + 0 + ), + [packageInfo.assets] + ); + return ( @@ -129,7 +182,6 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { {installedVersion} - {updateAvailable && } @@ -147,15 +199,21 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { - {updateAvailable && ( -

- -

+ {(updateAvailable || isUpgradingPackagePolicies) && ( + <> + + +

+ +

+ )} )} @@ -189,8 +247,9 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => {

-

@@ -220,8 +279,9 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => {

- diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx new file mode 100644 index 000000000000..00b6ccc2f791 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { InstallStatus } from '../../../../../types'; +import type { PackageInfo } from '../../../../../types'; + +import { + useCapabilities, + useGetPackageInstallStatus, + useUninstallPackage, +} from '../../../../../hooks'; + +import { ConfirmPackageUninstall } from './confirm_package_uninstall'; + +interface UninstallButtonProps extends Pick { + disabled?: boolean; + latestVersion?: string; + numOfAssets: number; +} + +export const UninstallButton: React.FunctionComponent = ({ + disabled = false, + latestVersion, + name, + numOfAssets, + title, + version, +}) => { + const hasWriteCapabilites = useCapabilities().write; + const uninstallPackage = useUninstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + const isRemoving = installationStatus === InstallStatus.uninstalling; + + const [isUninstallModalVisible, setIsUninstallModalVisible] = useState(false); + + const handleClickUninstall = useCallback(() => { + uninstallPackage({ name, version, title, redirectToVersion: latestVersion ?? version }); + setIsUninstallModalVisible(false); + }, [uninstallPackage, name, title, version, latestVersion]); + + const uninstallModal = ( + setIsUninstallModalVisible(false)} + onConfirm={handleClickUninstall} + /> + ); + + return hasWriteCapabilites ? ( + <> + setIsUninstallModalVisible(true)} + color="danger" + disabled={disabled || isRemoving ? true : false} + > + {isRemoving ? ( + + ) : ( + + )} + + {isUninstallModalVisible && uninstallModal} + + ) : null; +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx new file mode 100644 index 000000000000..8cdb3ece3062 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx @@ -0,0 +1,310 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, useEffect, useMemo, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiCheckbox, + EuiCallOut, + EuiConfirmModal, + EuiSpacer, +} from '@elastic/eui'; +import { sumBy } from 'lodash'; + +import type { + GetAgentPoliciesResponse, + PackageInfo, + UpgradePackagePolicyDryRunResponse, +} from '../../../../../types'; +import { InstallStatus } from '../../../../../types'; +import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../constants'; +import { + sendGetAgentPolicies, + useInstallPackage, + useGetPackageInstallStatus, + sendUpgradePackagePolicy, + useStartServices, + useCapabilities, + useLink, +} from '../../../../../hooks'; +import { toMountPoint } from '../../../../../../../../../../../src/plugins/kibana_react/public'; + +interface UpdateButtonProps extends Pick { + dryRunData?: UpgradePackagePolicyDryRunResponse | null; + packagePolicyIds?: string[]; + isUpgradingPackagePolicies?: boolean; + setIsUpgradingPackagePolicies?: React.Dispatch>; +} + +/* + + Updating an integration to a new version entails a bit of logic. We allow the user to choose whether they'd like to + simultaneously upgrade any package policies that include the current version of the integration. For example, if + a user is running four agent policies that include the `nginx-0.2.4` package and they update to `nginx-0.7.0`, they + can elect to also deploy the new integration version to any agent running one of those four agent policies. + + If the user does not elect to upgrade their running policies, we simply install the latest version of the package and + navigate to the new version's settings page, e.g. `/detail/nginx-0.7.0/settings`. + + If the user _does_ elect to upgrade their running policies, we display a confirmation modal. In this modal, we'll report the + number of agents and policies that will be affected by the upgrade, and if there are any conflicts. In the case of a conflict + between versions, an upgrade for a given package policy will be skipped and the user will need to manually recreate their policy + to resolve any breaking changes between versions. Once the user confirms, we first install the latest version of the integration, + then we make a call to the "upgrade policies" API endpoint with a list of all package policy ID's that include the current version + of the integration. This API endpoint will complete the upgrade process in bulk for each package policy provided. Upon completion, + we navigate to the new version's settings page, as above. + +*/ + +export const UpdateButton: React.FunctionComponent = ({ + dryRunData, + isUpgradingPackagePolicies = false, + name, + packagePolicyIds = [], + setIsUpgradingPackagePolicies = () => {}, + title, + version, +}) => { + const history = useHistory(); + const { getPath } = useLink(); + + const { notifications } = useStartServices(); + const hasWriteCapabilites = useCapabilities().write; + + const installPackage = useInstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + const isInstalling = installationStatus === InstallStatus.installing; + + const [isUpdateModalVisible, setIsUpdateModalVisible] = useState(false); + const [upgradePackagePolicies, setUpgradePackagePolicies] = useState(true); + const [agentPolicyData, setAgentPolicyData] = useState(); + + useEffect(() => { + const fetchAgentPolicyData = async () => { + if (packagePolicyIds && packagePolicyIds.length > 0) { + const { data } = await sendGetAgentPolicies({ + perPage: 1000, + page: 1, + // Fetch all agent policies that include one of the eligible package policies + kuery: `${AGENT_POLICY_SAVED_OBJECT_TYPE}.package_policies:${packagePolicyIds + .map((id) => `"${id}"`) + .join(' or ')}`, + }); + + setAgentPolicyData(data); + } + }; + + fetchAgentPolicyData(); + }, [packagePolicyIds]); + + const packagePolicyCount = useMemo(() => packagePolicyIds.length, [packagePolicyIds]); + const agentCount = useMemo( + () => sumBy(agentPolicyData?.items, ({ agents }) => agents ?? 0), + [agentPolicyData] + ); + const conflictCount = useMemo( + () => dryRunData?.filter((item) => item.hasErrors).length, + [dryRunData] + ); + + const handleUpgradePackagePoliciesChange = useCallback(() => { + setUpgradePackagePolicies((prev) => !prev); + }, []); + + const navigateToNewSettingsPage = useCallback(() => { + const settingsPath = getPath('integration_details_settings', { + pkgkey: `${name}-${version}`, + }); + history.push(settingsPath); + }, [history, getPath, name, version]); + + const handleClickUpdate = useCallback(async () => { + await installPackage({ name, version, title, fromUpdate: true }); + }, [installPackage, name, title, version]); + + const handleClickUpgradePolicies = useCallback(async () => { + if (isUpgradingPackagePolicies) { + return; + } + + setIsUpgradingPackagePolicies(true); + + await installPackage({ name, version, title }); + + await sendUpgradePackagePolicy( + // Only upgrade policies that don't have conflicts + packagePolicyIds.filter( + (id) => !dryRunData?.find((dryRunRecord) => dryRunRecord.diff?.[0].id === id)?.hasErrors + ) + ); + + setIsUpgradingPackagePolicies(false); + setIsUpdateModalVisible(false); + + notifications.toasts.addSuccess({ + title: toMountPoint( + + ), + text: toMountPoint( + + ), + }); + + navigateToNewSettingsPage(); + }, [ + dryRunData, + installPackage, + isUpgradingPackagePolicies, + name, + navigateToNewSettingsPage, + notifications.toasts, + packagePolicyIds, + setIsUpgradingPackagePolicies, + title, + version, + ]); + + const updateModal = ( + { + setIsUpdateModalVisible(false); + }} + cancelButtonText={i18n.translate( + 'xpack.fleet.integrations.settings.confirmUpdateModal.cancel', + { defaultMessage: 'Cancel' } + )} + onConfirm={handleClickUpgradePolicies} + confirmButtonText={i18n.translate( + 'xpack.fleet.integrations.settings.confirmUpdateModal.confirm', + { defaultMessage: 'Upgrade {packageName} and policies', values: { packageName: title } } + )} + title={i18n.translate('xpack.fleet.integrations.settings.confirmUpdateModal.updateTitle', { + defaultMessage: 'Upgrade {packageName} and policies', + values: { packageName: title }, + })} + > + <> + {conflictCount && conflictCount > 0 ? ( + <> + + + + {' '} + + + + + + ) : null} + + + + ), + agentCountText: ( + + + + ), + }} + /> + + + ); + + return hasWriteCapabilites ? ( + <> + + + setIsUpdateModalVisible(true) : handleClickUpdate + } + > + + + + {packagePolicyCount > 0 && ( + + + + )} + + + {isUpdateModalVisible && updateModal} + + ) : null; +}; diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index eefa3c870f28..7dc313970ef2 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -117,7 +117,10 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ disabled={!hasWriteCapabilities} icon="trash" onClick={() => { - deletePackagePoliciesPrompt([packagePolicy.id], refreshAgentPolicy); + deletePackagePoliciesPrompt([packagePolicy.id], () => { + setIsActionsMenuOpen(false); + refreshAgentPolicy(); + }); }} > }); }; +export const sendGetAgentPolicies = (query?: GetAgentPoliciesRequest['query']) => { + return sendRequest({ + path: agentPolicyRouteService.getListPath(), + method: 'get', + query, + }); +}; + export const useGetOneAgentPolicy = (agentPolicyId: string | undefined) => { return useConditionalRequest({ path: agentPolicyId ? agentPolicyRouteService.getInfoPath(agentPolicyId) : undefined, diff --git a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts index d39b15a3b3bf..f8d14647439b 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts @@ -66,14 +66,23 @@ export const sendGetOnePackagePolicy = (packagePolicyId: string) => { }); }; -export function sendUpgradePackagePolicyDryRun(packagePolicyIds: string[]) { +export function sendUpgradePackagePolicyDryRun( + packagePolicyIds: string[], + packageVersion?: string +) { + const body: { packagePolicyIds: string[]; dryRun: boolean; packageVersion?: string } = { + packagePolicyIds, + dryRun: true, + }; + + if (packageVersion) { + body.packageVersion = packageVersion; + } + return sendRequest({ path: packagePolicyRouteService.getUpgradePath(), method: 'post', - body: JSON.stringify({ - packagePolicyIds, - dryRun: true, - }), + body: JSON.stringify(body), }); } From b34c54e9344c2e6b29e684ad60d68ea13824a59d Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 20 Sep 2021 13:21:33 -0400 Subject: [PATCH 02/36] [App Search] Automated Curations: Split Curations view into "Overview" and "Settings" tabs (#112488) --- .../components/curations_table.test.tsx | 143 ++++++++++++++++++ .../curations/components/curations_table.tsx | 117 ++++++++++++++ .../components/curations/components/index.ts | 1 + .../curations/curations_logic.test.ts | 14 ++ .../components/curations/curations_logic.ts | 11 ++ .../curations/views/curations.test.tsx | 135 +++++------------ .../components/curations/views/curations.tsx | 142 +++++------------ .../views/curations_overview.test.tsx | 47 ++++++ .../curations/views/curations_overview.tsx | 27 ++++ .../views/curations_settings.test.tsx | 27 ++++ .../curations/views/curations_settings.tsx | 12 ++ .../test_helpers/get_page_header.tsx | 21 ++- .../public/applications/test_helpers/index.ts | 1 + 13 files changed, 491 insertions(+), 207 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx new file mode 100644 index 000000000000..0b647bffb3e2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; +import '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow, ReactWrapper } from 'enzyme'; + +import { EuiBasicTable } from '@elastic/eui'; + +import { mountWithIntl } from '../../../../test_helpers'; + +import { CurationsTable } from './curations_table'; + +describe('CurationsTable', () => { + const { navigateToUrl } = mockKibanaValues; + + const values = { + dataLoading: false, + curations: [ + { + id: 'cur-id-1', + last_updated: 'January 1, 1970 at 12:00PM', + queries: ['hiking'], + }, + { + id: 'cur-id-2', + last_updated: 'January 2, 1970 at 12:00PM', + queries: ['mountains', 'valleys'], + }, + ], + meta: { + page: { + current: 1, + size: 10, + total_results: 2, + }, + }, + }; + + const actions = { + deleteCuration: jest.fn(), + onPaginate: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('passes loading prop based on dataLoading', () => { + setMockValues({ ...values, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); + }); + + describe('populated table render', () => { + let wrapper: ReactWrapper; + + beforeAll(() => { + wrapper = mountWithIntl(); + }); + + it('renders queries and last updated columns', () => { + const tableContent = wrapper.find(EuiBasicTable).text(); + + expect(tableContent).toContain('Queries'); + expect(tableContent).toContain('hiking'); + expect(tableContent).toContain('mountains, valleys'); + + expect(tableContent).toContain('Last updated'); + expect(tableContent).toContain('Jan 1, 1970 12:00 PM'); + expect(tableContent).toContain('Jan 2, 1970 12:00 PM'); + }); + + it('renders queries with curation links', () => { + expect( + wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').first().prop('to') + ).toEqual('/engines/some-engine/curations/cur-id-1'); + + expect( + wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').last().prop('to') + ).toEqual('/engines/some-engine/curations/cur-id-2'); + }); + + describe('action column', () => { + it('edit action navigates to curation link', () => { + wrapper.find('[data-test-subj="CurationsTableEditButton"]').first().simulate('click'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-1'); + + wrapper.find('[data-test-subj="CurationsTableEditButton"]').last().simulate('click'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-2'); + }); + + it('delete action calls deleteCuration', () => { + wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').first().simulate('click'); + expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-1'); + + wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').last().simulate('click'); + expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-2'); + }); + }); + }); + + describe('pagination', () => { + it('passes pagination props from meta.page', () => { + setMockValues({ + ...values, + meta: { + page: { + current: 5, + size: 10, + total_results: 50, + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiBasicTable).prop('pagination')).toEqual({ + pageIndex: 4, + pageSize: 10, + totalItemCount: 50, + hidePerPageOptions: true, + }); + }); + + it('calls onPaginate on pagination change', () => { + const wrapper = shallow(); + wrapper.find(EuiBasicTable).simulate('change', { page: { index: 0 } }); + + expect(actions.onPaginate).toHaveBeenCalledWith(1); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx new file mode 100644 index 000000000000..ad508bea1dbc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx @@ -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 React from 'react'; + +import { useValues, useActions } from 'kea'; + +import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../shared/react_router_helpers'; +import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; + +import { ENGINE_CURATION_PATH } from '../../../routes'; +import { FormattedDateTime } from '../../../utils/formatted_date_time'; +import { generateEnginePath } from '../../engine'; + +import { CurationsLogic } from '../curations_logic'; +import { Curation } from '../types'; +import { convertToDate } from '../utils'; + +export const CurationsTable: React.FC = () => { + const { dataLoading, curations, meta } = useValues(CurationsLogic); + const { onPaginate, deleteCuration } = useActions(CurationsLogic); + + const columns: Array> = [ + { + field: 'queries', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.queries', + { defaultMessage: 'Queries' } + ), + render: (queries: Curation['queries'], curation: Curation) => ( + + {queries.join(', ')} + + ), + width: '40%', + truncateText: true, + mobileOptions: { + header: true, + // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error + // @ts-ignore + enlarge: true, + width: '100%', + truncateText: false, + }, + }, + { + field: 'last_updated', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.lastUpdated', + { defaultMessage: 'Last updated' } + ), + width: '30%', + dataType: 'string', + render: (dateString: string) => , + }, + { + width: '120px', + actions: [ + { + name: EDIT_BUTTON_LABEL, + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.editTooltip', + { defaultMessage: 'Edit curation' } + ), + type: 'icon', + icon: 'pencil', + color: 'primary', + onClick: (curation: Curation) => { + const { navigateToUrl } = KibanaLogic.values; + const url = generateEnginePath(ENGINE_CURATION_PATH, { curationId: curation.id }); + navigateToUrl(url); + }, + 'data-test-subj': 'CurationsTableEditButton', + }, + { + name: DELETE_BUTTON_LABEL, + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.deleteTooltip', + { defaultMessage: 'Delete curation' } + ), + type: 'icon', + icon: 'trash', + color: 'danger', + onClick: (curation: Curation) => deleteCuration(curation.id), + 'data-test-subj': 'CurationsTableDeleteButton', + }, + ], + }, + ]; + + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts index 8c9e58e6ba0f..473162dcbd91 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts @@ -5,4 +5,5 @@ * 2.0. */ +export { CurationsTable } from './curations_table'; export { EmptyState } from './empty_state'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts index f4296d4ff208..0d02fbe41387 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts @@ -50,6 +50,7 @@ describe('CurationsLogic', () => { dataLoading: true, curations: [], meta: DEFAULT_META, + selectedPageTab: 'overview', }; beforeEach(() => { @@ -89,6 +90,19 @@ describe('CurationsLogic', () => { }); }); }); + + describe('onSelectPageTab', () => { + it('should set the selected page tab', () => { + mount(); + + CurationsLogic.actions.onSelectPageTab('settings'); + + expect(CurationsLogic.values).toEqual({ + ...DEFAULT_VALUES, + selectedPageTab: 'settings', + }); + }); + }); }); describe('listeners', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts index a98f83396b3b..76adfa4b6ed4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts @@ -23,10 +23,13 @@ import { EngineLogic, generateEnginePath } from '../engine'; import { DELETE_MESSAGE, SUCCESS_MESSAGE } from './constants'; import { Curation, CurationsAPIResponse } from './types'; +type CurationsPageTabs = 'overview' | 'settings'; + interface CurationsValues { dataLoading: boolean; curations: Curation[]; meta: Meta; + selectedPageTab: CurationsPageTabs; } interface CurationsActions { @@ -35,6 +38,7 @@ interface CurationsActions { loadCurations(): void; deleteCuration(id: string): string; createCuration(queries: Curation['queries']): Curation['queries']; + onSelectPageTab(pageTab: CurationsPageTabs): { pageTab: CurationsPageTabs }; } export const CurationsLogic = kea>({ @@ -45,8 +49,15 @@ export const CurationsLogic = kea id, createCuration: (queries) => queries, + onSelectPageTab: (pageTab) => ({ pageTab }), }), reducers: () => ({ + selectedPageTab: [ + 'overview', + { + onSelectPageTab: (_, { pageTab }) => pageTab, + }, + ], dataLoading: [ true, { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index 85827d537417..effd03e2e743 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -5,23 +5,23 @@ * 2.0. */ -import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; +import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; import '../../../../__mocks__/react_router'; import '../../../__mocks__/engine_logic.mock'; import React from 'react'; -import { shallow, ReactWrapper } from 'enzyme'; +import { shallow } from 'enzyme'; -import { EuiBasicTable } from '@elastic/eui'; +import { EuiTab } from '@elastic/eui'; -import { mountWithIntl, getPageTitle } from '../../../../test_helpers'; +import { mountWithIntl, getPageHeaderTabs, getPageTitle } from '../../../../test_helpers'; -import { Curations, CurationsTable } from './curations'; +import { Curations } from './curations'; +import { CurationsOverview } from './curations_overview'; +import { CurationsSettings } from './curations_settings'; describe('Curations', () => { - const { navigateToUrl } = mockKibanaValues; - const values = { dataLoading: false, curations: [ @@ -43,12 +43,13 @@ describe('Curations', () => { total_results: 2, }, }, + selectedPageTab: 'overview', }; const actions = { loadCurations: jest.fn(), - deleteCuration: jest.fn(), onPaginate: jest.fn(), + onSelectPageTab: jest.fn(), }; beforeEach(() => { @@ -57,11 +58,38 @@ describe('Curations', () => { setMockActions(actions); }); - it('renders', () => { + it('renders with a set of tabs in the page header', () => { const wrapper = shallow(); expect(getPageTitle(wrapper)).toEqual('Curated results'); - expect(wrapper.find(CurationsTable)).toHaveLength(1); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + tabs.at(0).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(1, 'overview'); + + tabs.at(1).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'settings'); + }); + + it('renders an overview view', () => { + setMockValues({ ...values, selectedPageTab: 'overview' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(0).prop('isSelected')).toEqual(true); + + expect(wrapper.find(CurationsOverview)).toHaveLength(1); + }); + + it('renders a settings view', () => { + setMockValues({ ...values, selectedPageTab: 'settings' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(1).prop('isSelected')).toEqual(true); + + expect(wrapper.find(CurationsSettings)).toHaveLength(1); }); describe('loading state', () => { @@ -86,91 +114,4 @@ describe('Curations', () => { expect(actions.loadCurations).toHaveBeenCalledTimes(1); }); - - describe('CurationsTable', () => { - it('passes loading prop based on dataLoading', () => { - setMockValues({ ...values, dataLoading: true }); - const wrapper = shallow(); - - expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); - }); - - describe('populated table render', () => { - let wrapper: ReactWrapper; - - beforeAll(() => { - wrapper = mountWithIntl(); - }); - - it('renders queries and last updated columns', () => { - const tableContent = wrapper.find(EuiBasicTable).text(); - - expect(tableContent).toContain('Queries'); - expect(tableContent).toContain('hiking'); - expect(tableContent).toContain('mountains, valleys'); - - expect(tableContent).toContain('Last updated'); - expect(tableContent).toContain('Jan 1, 1970 12:00 PM'); - expect(tableContent).toContain('Jan 2, 1970 12:00 PM'); - }); - - it('renders queries with curation links', () => { - expect( - wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').first().prop('to') - ).toEqual('/engines/some-engine/curations/cur-id-1'); - - expect( - wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').last().prop('to') - ).toEqual('/engines/some-engine/curations/cur-id-2'); - }); - - describe('action column', () => { - it('edit action navigates to curation link', () => { - wrapper.find('[data-test-subj="CurationsTableEditButton"]').first().simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-1'); - - wrapper.find('[data-test-subj="CurationsTableEditButton"]').last().simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-2'); - }); - - it('delete action calls deleteCuration', () => { - wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').first().simulate('click'); - expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-1'); - - wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').last().simulate('click'); - expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-2'); - }); - }); - }); - - describe('pagination', () => { - it('passes pagination props from meta.page', () => { - setMockValues({ - ...values, - meta: { - page: { - current: 5, - size: 10, - total_results: 50, - }, - }, - }); - const wrapper = shallow(); - - expect(wrapper.find(EuiBasicTable).prop('pagination')).toEqual({ - pageIndex: 4, - pageSize: 10, - totalItemCount: 50, - hidePerPageOptions: true, - }); - }); - - it('calls onPaginate on pagination change', () => { - const wrapper = shallow(); - wrapper.find(EuiBasicTable).simulate('change', { page: { index: 0 } }); - - expect(actions.onPaginate).toHaveBeenCalledWith(1); - }); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 12497ab52baf..9584b21424fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -9,28 +9,47 @@ import React, { useEffect } from 'react'; import { useValues, useActions } from 'kea'; -import { EuiBasicTable, EuiBasicTableColumn, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; -import { KibanaLogic } from '../../../../shared/kibana'; -import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers'; -import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; +import { EuiButtonTo } from '../../../../shared/react_router_helpers'; -import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes'; -import { FormattedDateTime } from '../../../utils/formatted_date_time'; +import { ENGINE_CURATIONS_NEW_PATH } from '../../../routes'; import { generateEnginePath } from '../../engine'; import { AppSearchPageTemplate } from '../../layout'; -import { EmptyState } from '../components'; import { CURATIONS_OVERVIEW_TITLE, CREATE_NEW_CURATION_TITLE } from '../constants'; import { CurationsLogic } from '../curations_logic'; -import { Curation } from '../types'; -import { getCurationsBreadcrumbs, convertToDate } from '../utils'; +import { getCurationsBreadcrumbs } from '../utils'; + +import { CurationsOverview } from './curations_overview'; +import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading, curations, meta } = useValues(CurationsLogic); - const { loadCurations } = useActions(CurationsLogic); + const { dataLoading, curations, meta, selectedPageTab } = useValues(CurationsLogic); + const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); + + const pageTabs = [ + { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.overviewPageTabLabel', + { + defaultMessage: 'Overview', + } + ), + isSelected: selectedPageTab === 'overview', + onClick: () => onSelectPageTab('overview'), + }, + { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.settingsPageTabLabel', + { + defaultMessage: 'Settings', + } + ), + isSelected: selectedPageTab === 'settings', + onClick: () => onSelectPageTab('settings'), + }, + ]; useEffect(() => { loadCurations(); @@ -50,105 +69,12 @@ export const Curations: React.FC = () => { {CREATE_NEW_CURATION_TITLE} , ], + tabs: pageTabs, }} isLoading={dataLoading && !curations.length} - isEmptyState={!curations.length} - emptyState={} > - - - + {selectedPageTab === 'overview' && } + {selectedPageTab === 'settings' && } ); }; - -export const CurationsTable: React.FC = () => { - const { dataLoading, curations, meta } = useValues(CurationsLogic); - const { onPaginate, deleteCuration } = useActions(CurationsLogic); - - const columns: Array> = [ - { - field: 'queries', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.queries', - { defaultMessage: 'Queries' } - ), - render: (queries: Curation['queries'], curation: Curation) => ( - - {queries.join(', ')} - - ), - width: '40%', - truncateText: true, - mobileOptions: { - header: true, - // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error - // @ts-ignore - enlarge: true, - width: '100%', - truncateText: false, - }, - }, - { - field: 'last_updated', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.lastUpdated', - { defaultMessage: 'Last updated' } - ), - width: '30%', - dataType: 'string', - render: (dateString: string) => , - }, - { - width: '120px', - actions: [ - { - name: EDIT_BUTTON_LABEL, - description: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.editTooltip', - { defaultMessage: 'Edit curation' } - ), - type: 'icon', - icon: 'pencil', - color: 'primary', - onClick: (curation: Curation) => { - const { navigateToUrl } = KibanaLogic.values; - const url = generateEnginePath(ENGINE_CURATION_PATH, { curationId: curation.id }); - navigateToUrl(url); - }, - 'data-test-subj': 'CurationsTableEditButton', - }, - { - name: DELETE_BUTTON_LABEL, - description: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.deleteTooltip', - { defaultMessage: 'Delete curation' } - ), - type: 'icon', - icon: 'trash', - color: 'danger', - onClick: (curation: Curation) => deleteCuration(curation.id), - 'data-test-subj': 'CurationsTableDeleteButton', - }, - ], - }, - ]; - - return ( - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx new file mode 100644 index 000000000000..a034c3b2a068 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.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 { setMockValues } from '../../../../__mocks__/kea_logic'; +import '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { CurationsTable, EmptyState } from '../components'; + +import { CurationsOverview } from './curations_overview'; + +describe('CurationsOverview', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders an empty message when there are no curations', () => { + setMockValues({ curations: [] }); + const wrapper = shallow(); + + expect(wrapper.is(EmptyState)).toBe(true); + }); + + it('renders a curations table when there are curations present', () => { + setMockValues({ + curations: [ + { + id: 'cur-id-1', + }, + { + id: 'cur-id-2', + }, + ], + }); + const wrapper = shallow(); + + expect(wrapper.find(CurationsTable)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx new file mode 100644 index 000000000000..ec67d06da476 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.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 { useValues } from 'kea'; + +import { EuiPanel } from '@elastic/eui'; + +import { CurationsTable, EmptyState } from '../components'; +import { CurationsLogic } from '../curations_logic'; + +export const CurationsOverview: React.FC = () => { + const { curations } = useValues(CurationsLogic); + + return curations.length ? ( + + + + ) : ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx new file mode 100644 index 000000000000..855570829cce --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.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 '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { CurationsSettings } from './curations_settings'; + +describe('CurationsSettings', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders empty', () => { + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx new file mode 100644 index 000000000000..4bff7f3b2ef5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.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 CurationsSettings: React.FC = () => { + return null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx b/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx index a251188b5cd9..94440ad4e61a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx @@ -9,7 +9,7 @@ import React, { Fragment } from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiPageHeaderProps } from '@elastic/eui'; +import { EuiPageHeaderProps, EuiTab } from '@elastic/eui'; /* * Given an AppSearchPageTemplate or WorkplaceSearchPageTemplate, these @@ -35,13 +35,30 @@ export const getPageHeaderActions = (wrapper: ShallowWrapper) => { return shallow(

- {actions.map((action: React.ReactNode, i) => ( + {actions.map((action, i) => ( {action} ))}
); }; +export const getPageHeaderTabs = (wrapper: ShallowWrapper) => { + // The tabs prop of EuiPageHeader takes an `Array` + // instead of an array of EuiTab jsx components + // These are then rendered inside of EuiPageHeader as EuiTabs + // See https://elastic.github.io/eui/#/layout/page-header#tabs-in-the-page-header + + const tabs = getPageHeader(wrapper).tabs || []; + + return shallow( +
+ {tabs.map((tabProps, i) => ( + + ))} +
+ ); +}; + export const getPageHeaderChildren = (wrapper: ShallowWrapper) => { const children = getPageHeader(wrapper).children || null; diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts b/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts index 7903b4a31c8a..35836d552661 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts @@ -16,6 +16,7 @@ export { getPageDescription, getPageHeaderActions, getPageHeaderChildren, + getPageHeaderTabs, } from './get_page_header'; // Misc From 90ec827147ad5517bcb2a2e7be8918a59ee087a6 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 20 Sep 2021 13:59:27 -0400 Subject: [PATCH 03/36] [Alerting] Add telemetry for HTTP API calls with legacy terminology (#112173) * Deprecated attributes telemetry * Add additional term and improve test quality * PR feedback --- .../server/routes/aggregate_rules.test.ts | 43 +++++++++++++++++ .../alerting/server/routes/aggregate_rules.ts | 9 +++- .../alerting/server/routes/find_rules.test.ts | 42 ++++++++++++++++ .../alerting/server/routes/find_rules.ts | 12 ++++- .../plugins/alerting/server/routes/index.ts | 4 +- .../server/routes/legacy/aggregate.test.ts | 30 ++++++++++++ .../server/routes/legacy/aggregate.ts | 5 ++ .../server/routes/legacy/find.test.ts | 34 +++++++++++++ .../alerting/server/routes/legacy/find.ts | 7 +++ .../lib/track_legacy_terminology.test.ts | 48 +++++++++++++++++++ .../routes/lib/track_legacy_terminology.ts | 35 ++++++++++++++ 11 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts create mode 100644 x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts index 233e605cf7f5..c8d4372fb72c 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -11,13 +11,21 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); +const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); +const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('./lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -147,4 +155,39 @@ describe('aggregateRulesRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateRulesRoute(router, licenseState, mockUsageCounter); + const aggregateResult = { + alertExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + }; + rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index f91231f9ac48..84c03e21ff36 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -7,10 +7,12 @@ import { IRouter } from 'kibana/server'; import { schema } from '@kbn/config-schema'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; import { ILicenseState } from '../lib'; import { AggregateResult, AggregateOptions } from '../rules_client'; import { RewriteResponseCase, RewriteRequestCase, verifyAccessAndContext } from './lib'; import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -53,7 +55,8 @@ const rewriteBodyRes: RewriteResponseCase = ({ export const aggregateRulesRoute = ( router: IRouter, - licenseState: ILicenseState + licenseState: ILicenseState, + usageCounter?: UsageCounter ) => { router.get( { @@ -69,6 +72,10 @@ export const aggregateRulesRoute = ( ...req.query, has_reference: req.query.has_reference || undefined, }); + trackLegacyTerminology( + [req.query.search, req.query.search_fields].filter(Boolean) as string[], + usageCounter + ); const aggregateResult = await rulesClient.aggregate({ options }); return res.ok({ body: rewriteBodyRes(aggregateResult), diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index e0da7b064574..23f636e96216 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -11,13 +11,21 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); +const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); +const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('./lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -145,4 +153,38 @@ describe('findRulesRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState, mockUsageCounter); + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + sort_field: 'alertTypeId', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + 'alertTypeId', + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index ef6234129022..22d701b2040a 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -7,11 +7,13 @@ import { omit } from 'lodash'; import { IRouter } from 'kibana/server'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../lib'; import { FindOptions, FindResult } from '../rules_client'; import { RewriteRequestCase, RewriteResponseCase, verifyAccessAndContext } from './lib'; import { AlertTypeParams, AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; // query definition const querySchema = schema.object({ @@ -107,7 +109,8 @@ const rewriteBodyRes: RewriteResponseCase> = ({ export const findRulesRoute = ( router: IRouter, - licenseState: ILicenseState + licenseState: ILicenseState, + usageCounter?: UsageCounter ) => { router.get( { @@ -120,6 +123,13 @@ export const findRulesRoute = ( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = context.alerting.getRulesClient(); + trackLegacyTerminology( + [req.query.search, req.query.search_fields, req.query.sort_field].filter( + Boolean + ) as string[], + usageCounter + ); + const options = rewriteQueryReq({ ...req.query, has_reference: req.query.has_reference || undefined, diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index c1c7eae45109..2db52fa2c143 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -38,7 +38,7 @@ export interface RouteOptions { } export function defineRoutes(opts: RouteOptions) { - const { router, licenseState, encryptedSavedObjects } = opts; + const { router, licenseState, encryptedSavedObjects, usageCounter } = opts; defineLegacyRoutes(opts); createRuleRoute(opts); @@ -49,7 +49,7 @@ export function defineRoutes(opts: RouteOptions) { aggregateRulesRoute(router, licenseState); disableRuleRoute(router, licenseState); enableRuleRoute(router, licenseState); - findRulesRoute(router, licenseState); + findRulesRoute(router, licenseState, usageCounter); getRuleAlertSummaryRoute(router, licenseState); getRuleStateRoute(router, licenseState); healthRoute(router, licenseState, encryptedSavedObjects); diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts index d08e970eef69..cab779a42ce2 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts @@ -12,6 +12,7 @@ import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from './../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); @@ -26,6 +27,10 @@ jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('../lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -166,4 +171,29 @@ describe('aggregateAlertRoute', () => { await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('aggregate', mockUsageCounter); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts index d1e8d98a9540..bd4aa7750738 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts @@ -14,6 +14,7 @@ import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; import { renameKeys } from './../lib/rename_keys'; import { FindOptions } from '../../rules_client'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -55,6 +56,10 @@ export const aggregateAlertRoute = ( const rulesClient = context.alerting.getRulesClient(); trackLegacyRouteUsage('aggregate', usageCounter); + trackLegacyTerminology( + [req.query.search, req.query.search_fields].filter(Boolean) as string[], + usageCounter + ); const query = req.query; const renameMap = { diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts index de12c62f1e4f..1ddd1a662cbe 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -12,6 +12,7 @@ import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from './../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; const rulesClient = rulesClientMock.create(); @@ -23,6 +24,10 @@ jest.mock('../../lib/track_legacy_route_usage', () => ({ trackLegacyRouteUsage: jest.fn(), })); +jest.mock('../lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -160,4 +165,33 @@ describe('findAlertRoute', () => { await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('find', mockUsageCounter); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + findAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + sort_field: 'alertTypeId', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + 'alertTypeId', + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index f915f0e15afb..5a87536a1ac9 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -15,6 +15,7 @@ import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; import { renameKeys } from './../lib/rename_keys'; import { FindOptions } from '../../rules_client'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -59,6 +60,12 @@ export const findAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('find', usageCounter); + trackLegacyTerminology( + [req.query.search, req.query.search_fields, req.query.sort_field].filter( + Boolean + ) as string[], + usageCounter + ); const rulesClient = context.alerting.getRulesClient(); const query = req.query; diff --git a/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts new file mode 100644 index 000000000000..5c6838e144aa --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.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 { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; +import { trackLegacyTerminology, LEGACY_TERMS } from './track_legacy_terminology'; + +describe('trackLegacyTerminology', () => { + it('should call `usageCounter.incrementCounter`', () => { + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + trackLegacyTerminology( + ['shouldNotMatch', LEGACY_TERMS.map((lt) => `${lt}foo`)], + mockUsageCounter + ); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledTimes(LEGACY_TERMS.length); + LEGACY_TERMS.forEach((legacyTerm, index) => { + expect((mockUsageCounter.incrementCounter as jest.Mock).mock.calls[index][0]).toStrictEqual({ + counterName: `legacyTerm_${legacyTerm}`, + counterType: 'legacyTerminology', + incrementBy: 1, + }); + }); + }); + + it('should do nothing if no usage counter is provided', () => { + let err; + try { + trackLegacyTerminology(['test'], undefined); + } catch (e) { + err = e; + } + expect(err).toBeUndefined(); + }); + + it('should do nothing if no terms are provided', () => { + let err; + try { + trackLegacyTerminology([], undefined); + } catch (e) { + err = e; + } + expect(err).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts new file mode 100644 index 000000000000..e825cb990fc4 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.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 { flatten } from 'lodash'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; + +export const LEGACY_TERMS = ['alertTypeId', 'actionTypeId']; + +export function trackLegacyTerminology( + terms: Array, + usageCounter?: UsageCounter +) { + if (!usageCounter) { + return null; + } + + if (!terms || terms.length === 0) { + return null; + } + + for (const legacyTerm of LEGACY_TERMS) { + for (const term of flatten(terms)) { + if (term.includes(legacyTerm)) { + usageCounter.incrementCounter({ + counterName: `legacyTerm_${legacyTerm}`, + counterType: 'legacyTerminology', + incrementBy: 1, + }); + } + } + } +} From 6f2815a76498e06e1180f7f30098fae3f225dc84 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 20 Sep 2021 20:13:09 +0200 Subject: [PATCH 04/36] [Discover] Improve saved query management functional service (#112055) --- test/functional/services/saved_query_management_component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/services/saved_query_management_component.ts b/test/functional/services/saved_query_management_component.ts index 37c6a45557f2..a216f8cb0469 100644 --- a/test/functional/services/saved_query_management_component.ts +++ b/test/functional/services/saved_query_management_component.ts @@ -166,8 +166,9 @@ export class SavedQueryManagementComponentService extends FtrService { const isOpenAlready = await this.testSubjects.exists('saved-query-management-popover'); if (isOpenAlready) return; + await this.testSubjects.click('saved-query-management-popover-button'); + await this.retry.waitFor('saved query management popover to have any text', async () => { - await this.testSubjects.click('saved-query-management-popover-button'); const queryText = await this.testSubjects.getVisibleText('saved-query-management-popover'); return queryText.length > 0; }); From 10ac814d8f5ede2c10c83aadc99304a59ac9682f Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 20 Sep 2021 15:29:52 -0400 Subject: [PATCH 05/36] [Cases] Migrate user actions connector ID (#108272) * Making progress * Fleshing out the extraction logic * Finishing migration logic and starting more tests * Finishing migration unit tests * Making progress on services * Finishing transform to es schema * Finishing transform functionality and unit tests * reverting migration data updates * Cleaning up type errors * fixing test error * Working migration tests * Refactoring retrieval of connector fields * Refactoring connector id in and tests in frontend * Fixing tests and finished refactoring parse string * Fixing integration test * Fixing integration tests * Removing some duplicate code and updating test name * Fixing create connector user action bug * Addressing feedback and logging error * Moving parsing function to common * Fixing type errors * Fixing type errors * Addressing feedback * Fixing lint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/common/api/cases/case.ts | 16 +- .../cases/common/api/cases/user_actions.ts | 3 +- .../cases/common/api/connectors/index.ts | 12 +- x-pack/plugins/cases/common/index.ts | 1 + x-pack/plugins/cases/common/ui/types.ts | 2 + .../cases/common/utils/user_actions.ts | 18 + .../cases/public/common/user_actions/index.ts | 8 + .../common/user_actions/parsers.test.ts | 86 + .../public/common/user_actions/parsers.ts | 77 + .../components/edit_connector/helpers.test.ts | 187 ++ .../components/edit_connector/helpers.ts | 30 +- .../user_action_tree/helpers.test.tsx | 108 +- .../components/user_action_tree/helpers.tsx | 53 +- .../components/user_action_tree/index.tsx | 10 +- .../plugins/cases/public/containers/mock.ts | 123 +- .../use_get_case_user_actions.test.tsx | 237 +- .../containers/use_get_case_user_actions.tsx | 51 +- .../plugins/cases/public/containers/utils.ts | 8 - .../cases/server/client/attachments/add.ts | 6 +- .../cases/server/client/attachments/update.ts | 9 +- .../cases/server/client/cases/create.ts | 2 +- .../cases/server/client/cases/delete.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 18 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 6 +- .../cases/server/client/cases/utils.ts | 30 +- .../server/client/user_actions/get.test.ts | 106 + .../cases/server/client/user_actions/get.ts | 47 +- .../plugins/cases/server/common/constants.ts | 29 + .../migrations/cases.test.ts | 570 ++-- .../saved_object_types/migrations/cases.ts | 14 +- .../migrations/configuration.test.ts | 142 +- .../migrations/configuration.ts | 9 +- .../saved_object_types/migrations/index.ts | 57 +- .../migrations/user_actions.test.ts | 562 ++++ .../migrations/user_actions.ts | 159 + .../migrations/utils.test.ts | 229 -- .../saved_object_types/migrations/utils.ts | 73 - .../cases/server/services/cases/index.test.ts | 8 +- .../cases/server/services/test_utils.ts | 17 +- .../services/user_actions/helpers.test.ts | 332 ++ .../server/services/user_actions/helpers.ts | 192 +- .../services/user_actions/index.test.ts | 557 ++++ .../server/services/user_actions/index.ts | 101 +- .../services/user_actions/transform.test.ts | 1246 +++++++ .../server/services/user_actions/transform.ts | 320 ++ .../server/services/user_actions/types.ts | 14 + .../tests/common/cases/delete_cases.ts | 4 +- .../tests/common/cases/patch_cases.ts | 4 + .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/post_comment.ts | 2 + .../user_actions/get_all_user_actions.ts | 19 +- .../tests/common/user_actions/migrations.ts | 149 +- .../tests/trial/cases/push_case.ts | 3 +- .../user_actions/get_all_user_actions.ts | 4 +- .../migrations/7.13_user_actions/data.json.gz | Bin 0 -> 2078 bytes .../7.13_user_actions/mappings.json | 2954 +++++++++++++++++ 57 files changed, 7895 insertions(+), 1140 deletions(-) create mode 100644 x-pack/plugins/cases/common/utils/user_actions.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/index.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.test.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.ts create mode 100644 x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/client/user_actions/get.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/index.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/types.ts create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 37a491cdad4c..05a053307b29 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -87,8 +87,11 @@ const CaseBasicRt = rt.type({ owner: rt.string, }); -export const CaseExternalServiceBasicRt = rt.type({ - connector_id: rt.union([rt.string, rt.null]), +/** + * This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field + * within the user action object in the API response. + */ +export const CaseUserActionExternalServiceRt = rt.type({ connector_name: rt.string, external_id: rt.string, external_title: rt.string, @@ -97,7 +100,14 @@ export const CaseExternalServiceBasicRt = rt.type({ pushed_by: UserRT, }); -const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); +export const CaseExternalServiceBasicRt = rt.intersection([ + rt.type({ + connector_id: rt.union([rt.string, rt.null]), + }), + CaseUserActionExternalServiceRt, +]); + +export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); export const CaseAttributesRt = rt.intersection([ CaseBasicRt, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index 03912c550d77..e86ce5248a6f 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -34,7 +34,6 @@ const UserActionRt = rt.union([ rt.literal('push-to-service'), ]); -// TO DO change state to status const CaseUserActionBasicRT = rt.type({ action_field: UserActionFieldRt, action: UserActionRt, @@ -51,6 +50,8 @@ const CaseUserActionResponseRT = rt.intersection([ action_id: rt.string, case_id: rt.string, comment_id: rt.union([rt.string, rt.null]), + new_val_connector_id: rt.union([rt.string, rt.null]), + old_val_connector_id: rt.union([rt.string, rt.null]), }), rt.partial({ sub_case_id: rt.string }), ]); diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 77af90b5d08c..2b3483b4f618 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -84,14 +84,22 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorSwimlaneTypeFieldsRt, ]); +/** + * This type represents the connector's format when it is encoded within a user action. + */ +export const CaseUserActionConnectorRt = rt.intersection([ + rt.type({ name: rt.string }), + ConnectorTypeFieldsRt, +]); + export const CaseConnectorRt = rt.intersection([ rt.type({ id: rt.string, - name: rt.string, }), - ConnectorTypeFieldsRt, + CaseUserActionConnectorRt, ]); +export type CaseUserActionConnector = rt.TypeOf; export type CaseConnector = rt.TypeOf; export type ConnectorTypeFields = rt.TypeOf; export type ConnectorJiraTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index 5305318cc9aa..d38b1a779981 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -12,3 +12,4 @@ export * from './constants'; export * from './api'; export * from './ui/types'; export * from './utils/connectors_api'; +export * from './utils/user_actions'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index bf4ec0da6ee5..c89c3eb08263 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -66,7 +66,9 @@ export interface CaseUserActions { caseId: string; commentId: string | null; newValue: string | null; + newValConnectorId: string | null; oldValue: string | null; + oldValConnectorId: string | null; } export interface CaseExternalService { diff --git a/x-pack/plugins/cases/common/utils/user_actions.ts b/x-pack/plugins/cases/common/utils/user_actions.ts new file mode 100644 index 000000000000..7de0d7066eae --- /dev/null +++ b/x-pack/plugins/cases/common/utils/user_actions.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. + */ + +export function isCreateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'create' && actionFields != null && actionFields.includes('connector'); +} + +export function isUpdateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'update' && actionFields != null && actionFields.includes('connector'); +} + +export function isPush(action?: string, actionFields?: string[]): boolean { + return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed'); +} diff --git a/x-pack/plugins/cases/public/common/user_actions/index.ts b/x-pack/plugins/cases/public/common/user_actions/index.ts new file mode 100644 index 000000000000..507455f7102a --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/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 './parsers'; diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts new file mode 100644 index 000000000000..c6d13cc41686 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts @@ -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 { ConnectorTypes, noneConnectorId } from '../../../common'; +import { parseStringAsConnector, parseStringAsExternalService } from './parsers'; + +describe('user actions utility functions', () => { + describe('parseStringAsConnector', () => { + it('return null if the data is null', () => { + expect(parseStringAsConnector('', null)).toBeNull(); + }); + + it('return null if the data is not a json object', () => { + expect(parseStringAsConnector('', 'blah')).toBeNull(); + }); + + it('return null if the data is not a valid connector', () => { + expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('return null if id is null but the data is a connector other than none', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null }) + ) + ).toBeNull(); + }); + + it('return the id as the none connector if the data is the none connector', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }) + ) + ).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null }); + }); + + it('returns a decoded connector with the specified id', () => { + expect( + parseStringAsConnector( + 'a', + JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null }) + ) + ).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null }); + }); + }); + + describe('parseStringAsExternalService', () => { + it('returns null when the data is null', () => { + expect(parseStringAsExternalService('', null)).toBeNull(); + }); + + it('returns null when the data is not valid json', () => { + expect(parseStringAsExternalService('', 'blah')).toBeNull(); + }); + + it('returns null when the data is not a valid external service object', () => { + expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('returns the decoded external service with the connector_id field added', () => { + const externalServiceInfo = { + connector_name: 'name', + external_id: '1', + external_title: 'title', + external_url: 'abc', + pushed_at: '1', + pushed_by: { + username: 'a', + email: 'a@a.com', + full_name: 'a', + }, + }; + + expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({ + ...externalServiceInfo, + connector_id: '500', + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.ts new file mode 100644 index 000000000000..dfea22443aa5 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CaseUserActionConnectorRt, + CaseConnector, + ConnectorTypes, + noneConnectorId, + CaseFullExternalService, + CaseUserActionExternalServiceRt, +} from '../../../common'; + +export const parseStringAsConnector = ( + id: string | null, + encodedData: string | null +): CaseConnector | null => { + if (encodedData == null) { + return null; + } + + const decodedConnector = parseString(encodedData); + + if (!CaseUserActionConnectorRt.is(decodedConnector)) { + return null; + } + + if (id == null && decodedConnector.type === ConnectorTypes.none) { + return { + ...decodedConnector, + id: noneConnectorId, + }; + } else if (id == null) { + return null; + } else { + // id does not equal null or undefined and the connector type does not equal none + // so return the connector with its id + return { + ...decodedConnector, + id, + }; + } +}; + +const parseString = (params: string | null): unknown | null => { + if (params == null) { + return null; + } + + try { + return JSON.parse(params); + } catch { + return null; + } +}; + +export const parseStringAsExternalService = ( + id: string | null, + encodedData: string | null +): CaseFullExternalService => { + if (encodedData == null) { + return null; + } + + const decodedExternalService = parseString(encodedData); + if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) { + return null; + } + + return { + ...decodedExternalService, + connector_id: id, + }; +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts new file mode 100644 index 000000000000..e20d6b37258b --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionConnector, ConnectorTypes } from '../../../common'; +import { CaseUserActions } from '../../containers/types'; +import { getConnectorFieldsFromUserActions } from './helpers'; + +describe('helpers', () => { + describe('getConnectorFieldsFromUserActions', () => { + it('returns null when it cannot find the connector id', () => { + expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); + }); + + it('returns null when the value fields are not valid encoded fields', () => { + expect( + getConnectorFieldsFromUserActions('a', [createUserAction({ newValue: 'a', oldValue: 'a' })]) + ).toBeNull(); + }); + + it('returns null when it cannot find the connector id in a non empty array', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: JSON.stringify({ a: '1' }), + oldValue: JSON.stringify({ a: '1' }), + }), + ]) + ).toBeNull(); + }); + + it('returns the fields when it finds the connector id in the new value', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: JSON.stringify({ a: '1' }), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the new value and the old value is null', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the old value', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ + fields: expectedFields, + }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('returns the fields when it finds the connector id in the second user action', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector(), + newValConnectorId: 'b', + oldValConnectorId: 'a', + }), + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ fields: expectedFields }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('ignores a parse failure and finds the right user action', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: 'b', + newValConnectorId: null, + }), + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + + it('returns null when the id matches but the encoded value is null', () => { + expect( + getConnectorFieldsFromUserActions('b', [ + createUserAction({ + newValue: null, + newValConnectorId: 'b', + }), + ]) + ).toBeNull(); + }); + + it('returns null when the action fields is not of length 1', () => { + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: JSON.stringify({ a: '1', fields: { hello: '1' } }), + oldValue: JSON.stringify({ a: '1', fields: { hi: '2' } }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + actionField: ['connector', 'connector'], + }), + ]) + ).toBeNull(); + }); + + it('matches the none connector the searched for id is none', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + }); +}); + +function createUserAction(fields: Partial): CaseUserActions { + return { + action: 'update', + actionAt: '', + actionBy: {}, + actionField: ['connector'], + actionId: '', + caseId: '', + commentId: '', + newValConnectorId: null, + oldValConnectorId: null, + newValue: null, + oldValue: null, + ...fields, + }; +} + +function createEncodedJiraConnector(fields?: Partial): string { + return JSON.stringify({ + type: ConnectorTypes.jira, + name: 'name', + fields: defaultJiraFields, + ...fields, + }); +} + +const defaultJiraFields = { + issueType: '1', + parent: null, + priority: null, +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts index 36eb3f58c8aa..b97035c458ac 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts @@ -5,23 +5,33 @@ * 2.0. */ +import { ConnectorTypeFields } from '../../../common'; import { CaseUserActions } from '../../containers/types'; +import { parseStringAsConnector } from '../../common/user_actions'; -export const getConnectorFieldsFromUserActions = (id: string, userActions: CaseUserActions[]) => { +export const getConnectorFieldsFromUserActions = ( + id: string, + userActions: CaseUserActions[] +): ConnectorTypeFields['fields'] => { try { for (const action of [...userActions].reverse()) { if (action.actionField.length === 1 && action.actionField[0] === 'connector') { - if (action.oldValue && action.newValue) { - const oldValue = JSON.parse(action.oldValue); - const newValue = JSON.parse(action.newValue); + const parsedNewConnector = parseStringAsConnector( + action.newValConnectorId, + action.newValue + ); - if (newValue.id === id) { - return newValue.fields; - } + if (parsedNewConnector && id === parsedNewConnector.id) { + return parsedNewConnector.fields; + } + + const parsedOldConnector = parseStringAsConnector( + action.oldValConnectorId, + action.oldValue + ); - if (oldValue.id === id) { - return oldValue.fields; - } + if (parsedOldConnector && id === parsedOldConnector.id) { + return parsedOldConnector.fields; } } } diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx index b49a010cff38..841f0d36bbf1 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../common'; +import { CaseStatuses, ConnectorTypes } from '../../../common'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, @@ -129,7 +129,7 @@ describe('User action tree helpers', () => { `${i18n.PUSHED_NEW_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); @@ -142,50 +142,74 @@ describe('User action tree helpers', () => { `${i18n.UPDATE_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); - it('label title generated for update connector - change connector', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'resilient-2' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('selected My Connector 2 as incident management system'); - }); - - it('label title generated for update connector - change connector to none', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'none' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, + describe('getConnectorLabelTitle', () => { + it('returns an empty string when the encoded old value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { oldValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns an empty string when the encoded new value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { newValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns the change connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ + type: ConnectorTypes.serviceNowITSM, + name: 'a', + fields: null, + }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.resilient, name: 'a', fields: null }), + newValConnectorId: 'resilient-2', + }), + connectors, + }); + + expect(result).toEqual('selected My Connector 2 as incident management system'); + }); + + it('returns the removed connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }), + newValConnectorId: 'none', + }), + connectors, + }); + + expect(result).toEqual('removed external incident management system'); + }); + + it('returns the connector fields changed label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + newValConnectorId: 'servicenow-1', + }), + connectors, + }); + + expect(result).toEqual('changed connector field'); }); - - expect(result).toEqual('removed external incident management system'); - }); - - it('label title generated for update connector - field change', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'servicenow-1' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('changed connector field'); }); describe('toStringArray', () => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx index 744b14926b35..2eb44f91190c 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx @@ -23,10 +23,11 @@ import { CommentType, Comment, CommentRequestActionsType, + noneConnectorId, } from '../../../common'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../../common/user_actions'; import { Tags } from '../tag_list/tags'; import { UserActionUsernameWithAvatar } from './user_action_username_with_avatar'; import { UserActionTimestamp } from './user_action_timestamp'; @@ -97,23 +98,27 @@ export const getConnectorLabelTitle = ({ action: CaseUserActions; connectors: ActionConnector[]; }) => { - const oldValue = parseString(`${action.oldValue}`); - const newValue = parseString(`${action.newValue}`); + const oldConnector = parseStringAsConnector(action.oldValConnectorId, action.oldValue); + const newConnector = parseStringAsConnector(action.newValConnectorId, action.newValue); - if (oldValue === null || newValue === null) { + if (!oldConnector || !newConnector) { return ''; } - // Connector changed - if (oldValue.id !== newValue.id) { - const newConnector = connectors.find((c) => c.id === newValue.id); - return newValue.id != null && newValue.id !== 'none' && newConnector != null - ? i18n.SELECTED_THIRD_PARTY(newConnector.name) - : i18n.REMOVED_THIRD_PARTY; - } else { - // Field changed + // if the ids are the same, assume we just changed the fields + if (oldConnector.id === newConnector.id) { return i18n.CHANGED_CONNECTOR_FIELD; } + + // ids are not the same so check and see if the id is a valid connector and then return its name + // if the connector id is the none connector value then it must have been removed + const newConnectorActionInfo = connectors.find((c) => c.id === newConnector.id); + if (newConnector.id !== noneConnectorId && newConnectorActionInfo != null) { + return i18n.SELECTED_THIRD_PARTY(newConnectorActionInfo.name); + } + + // it wasn't a valid connector or it was the none connector, so it must have been removed + return i18n.REMOVED_THIRD_PARTY; }; const getTagsLabelTitle = (action: CaseUserActions) => { @@ -133,7 +138,8 @@ const getTagsLabelTitle = (action: CaseUserActions) => { }; export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: boolean) => { - const pushedVal = JSON.parse(action.newValue ?? '') as CaseFullExternalService; + const externalService = parseStringAsExternalService(action.newValConnectorId, action.newValue); + return ( {`${firstPush ? i18n.PUSHED_NEW_INCIDENT : i18n.UPDATE_INCIDENT} ${ - pushedVal?.connector_name + externalService?.connector_name }`} - - {pushedVal?.external_title} + + {externalService?.external_title} @@ -157,20 +163,19 @@ export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: b export const getPushInfo = ( caseServices: CaseServices, - // a JSON parse failure will result in null for parsedValue - parsedValue: { connector_id: string | null; connector_name: string } | null, + externalService: CaseFullExternalService | undefined, index: number ) => - parsedValue != null && parsedValue.connector_id != null + externalService != null && externalService.connector_id != null ? { - firstPush: caseServices[parsedValue.connector_id]?.firstPushIndex === index, - parsedConnectorId: parsedValue.connector_id, - parsedConnectorName: parsedValue.connector_name, + firstPush: caseServices[externalService.connector_id]?.firstPushIndex === index, + parsedConnectorId: externalService.connector_id, + parsedConnectorName: externalService.connector_name, } : { firstPush: false, - parsedConnectorId: 'none', - parsedConnectorName: 'none', + parsedConnectorId: noneConnectorId, + parsedConnectorName: noneConnectorId, }; const getUpdateActionIcon = (actionField: string): string => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx index 784817229caf..7ea415324194 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx @@ -35,7 +35,7 @@ import { Ecs, } from '../../../common'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsExternalService } from '../../common/user_actions'; import { OnUpdateFields } from '../case_view'; import { getConnectorLabelTitle, @@ -512,10 +512,14 @@ export const UserActionTree = React.memo( // Pushed information if (action.actionField.length === 1 && action.actionField[0] === 'pushed') { - const parsedValue = parseString(`${action.newValue}`); + const parsedExternalService = parseStringAsExternalService( + action.newValConnectorId, + action.newValue + ); + const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( caseServices, - parsedValue, + parsedExternalService, index ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index c955bb34240e..fcd564969d48 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -9,6 +9,7 @@ import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } import { AssociationType, + CaseUserActionConnector, CaseResponse, CasesFindResponse, CasesResponse, @@ -19,6 +20,9 @@ import { CommentResponse, CommentType, ConnectorTypes, + isCreateConnector, + isPush, + isUpdateConnector, SECURITY_SOLUTION_OWNER, UserAction, UserActionField, @@ -240,7 +244,9 @@ export const pushedCase: Case = { const basicAction = { actionAt: basicCreatedAt, actionBy: elasticUser, + oldValConnectorId: null, oldValue: null, + newValConnectorId: null, newValue: 'what a cool value', caseId: basicCaseId, commentId: null, @@ -308,12 +314,7 @@ export const basicCaseSnake: CaseResponse = { closed_at: null, closed_by: null, comments: [basicCommentSnake], - connector: { - id: 'none', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, + connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, created_at: basicCreatedAt, created_by: elasticUserSnake, external_service: null, @@ -328,8 +329,8 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; +export const pushConnectorId = '123'; export const pushSnake = { - connector_id: '123', connector_name: 'connector name', external_id: 'external_id', external_title: 'external title', @@ -350,7 +351,7 @@ export const pushedCaseSnake = { type: ConnectorTypes.jira, fields: null, }, - external_service: basicPushSnake, + external_service: { ...basicPushSnake, connector_id: pushConnectorId }, }; export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; @@ -385,17 +386,20 @@ const basicActionSnake = { comment_id: null, owner: SECURITY_SOLUTION_OWNER, }; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserActionSnake = (af: UserActionField, a: UserAction) => { + const isPushToService = a === 'push-to-service' && af[0] === 'pushed'; + + return { + ...basicActionSnake, + action_id: `${af[0]}-${a}`, + action_field: af, + action: a, + comment_id: af[0] === 'comment' ? basicCommentId : null, + new_value: isPushToService ? JSON.stringify(basicPushSnake) : basicAction.newValue, + new_val_connector_id: isPushToService ? pushConnectorId : null, + old_val_connector_id: null, + }; +}; export const caseUserActionsSnake: CaseUserActionsResponse = [ getUserActionSnake(['description'], 'create'), @@ -405,17 +409,76 @@ export const caseUserActionsSnake: CaseUserActionsResponse = [ // user actions -export const getUserAction = (af: UserActionField, a: UserAction) => ({ - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - newValue: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserAction = ( + af: UserActionField, + a: UserAction, + overrides?: Partial +): CaseUserActions => { + return { + ...basicAction, + actionId: `${af[0]}-${a}`, + actionField: af, + action: a, + commentId: af[0] === 'comment' ? basicCommentId : null, + ...getValues(a, af, overrides), + }; +}; + +const getValues = ( + userAction: UserAction, + actionFields: UserActionField, + overrides?: Partial +): Partial => { + if (isCreateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicCaseSnake) : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: null, + oldValConnectorId: null, + }; + } else if (isUpdateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined + ? JSON.stringify({ name: 'My Connector', type: ConnectorTypes.none, fields: null }) + : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: + overrides?.oldValue === undefined + ? JSON.stringify({ name: 'My Connector2', type: ConnectorTypes.none, fields: null }) + : overrides.oldValue, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else if (isPush(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicPushSnake) : overrides?.newValue, + newValConnectorId: + overrides?.newValConnectorId === undefined ? pushConnectorId : overrides.newValConnectorId, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else { + return { + newValue: overrides?.newValue === undefined ? basicAction.newValue : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } +}; + +export const getJiraConnectorWithoutId = (overrides?: Partial) => { + return JSON.stringify({ + name: 'jira1', + type: ConnectorTypes.jira, + ...jiraFields, + ...overrides, + }); +}; + +export const jiraFields = { fields: { issueType: '10006', priority: null, parent: null } }; export const getAlertUserAction = () => ({ ...basicAction, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index 62b4cf92434c..e7e46fa46c7c 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -18,7 +18,9 @@ import { basicPushSnake, caseUserActions, elasticUser, + getJiraConnectorWithoutId, getUserAction, + jiraFields, } from './mock'; import * as api from './api'; @@ -299,15 +301,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -346,15 +347,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -392,11 +392,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -418,11 +414,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123To456UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -444,16 +436,8 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - }, + createChangeConnector123To456UserAction(), + createChangeConnector456To123UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -474,22 +458,10 @@ describe('useGetCaseUserActions', () => { it('Change fields and connector after push - hasDataToPush: true', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -510,22 +482,10 @@ describe('useGetCaseUserActions', () => { it('Change only connector after push - hasDataToPush: false', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -547,45 +507,24 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector456To123PriorityLowUserAction(), + createChangeConnector123LowPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -617,34 +556,22 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + newValConnectorId: '456', newValue: JSON.stringify(push456), - }; + }); + const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -675,22 +602,10 @@ describe('useGetCaseUserActions', () => { it('Changing other connectors fields does not count as an update', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '3' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createUpdateConnectorFields456HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -709,3 +624,83 @@ describe('useGetCaseUserActions', () => { }); }); }); + +const jira123HighPriorityFields = { + fields: { ...jiraFields.fields, priority: 'High' }, +}; + +const jira123LowPriorityFields = { + fields: { ...jiraFields.fields, priority: 'Low' }, +}; + +const jira456Fields = { + fields: { issueType: '10', parent: null, priority: null }, +}; + +const jira456HighPriorityFields = { + fields: { ...jira456Fields.fields, priority: 'High' }, +}; + +const createUpdateConnectorFields123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValConnectorId: '123', + }); + +const createUpdateConnectorFields456HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + newValue: getJiraConnectorWithoutId(jira456HighPriorityFields), + oldValConnectorId: '456', + newValConnectorId: '456', + }); + +const createChangeConnector123HighPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123To456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123LowPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector456To123UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(), + newValConnectorId: '123', + }); + +const createChangeConnector456To123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + newValConnectorId: '123', + }); + +const createChangeConnector456To123PriorityLowUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + newValConnectorId: '123', + }); 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 e481519ba19a..36d600c3f1c9 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 @@ -18,7 +18,8 @@ import { } from '../../common'; import { getCaseUserActions, getSubCaseUserActions } from './api'; import * as i18n from './translations'; -import { convertToCamelCase, parseString } from './utils'; +import { convertToCamelCase } from './utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../common/user_actions'; import { useToasts } from '../common/lib/kibana'; export interface CaseService extends CaseExternalService { @@ -58,8 +59,24 @@ export interface UseGetCaseUserActions extends CaseUserActionsState { ) => Promise; } -const getExternalService = (value: string): CaseExternalService | null => - convertToCamelCase(parseString(`${value}`)); +const unknownExternalServiceConnectorId = 'unknown'; + +const getExternalService = ( + connectorId: string | null, + encodedValue: string | null +): CaseExternalService | null => { + const decodedValue = parseStringAsExternalService(connectorId, encodedValue); + + if (decodedValue == null) { + return null; + } + return { + ...convertToCamelCase(decodedValue), + // if in the rare case that the connector id is null we'll set it to unknown if we need to reference it in the UI + // anywhere. The id would only ever be null if a migration failed or some logic error within the backend occurred + connectorId: connectorId ?? unknownExternalServiceConnectorId, + }; +}; const groupConnectorFields = ( userActions: CaseUserActions[] @@ -69,22 +86,26 @@ const groupConnectorFields = ( return acc; } - const oldValue = parseString(`${mua.oldValue}`); - const newValue = parseString(`${mua.newValue}`); + const oldConnector = parseStringAsConnector(mua.oldValConnectorId, mua.oldValue); + const newConnector = parseStringAsConnector(mua.newValConnectorId, mua.newValue); - if (oldValue == null || newValue == null) { + if (!oldConnector || !newConnector) { return acc; } return { ...acc, - [oldValue.id]: [ - ...(acc[oldValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [oldValue.fields]), + [oldConnector.id]: [ + ...(acc[oldConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [oldConnector.fields]), ], - [newValue.id]: [ - ...(acc[newValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [newValue.fields]), + [newConnector.id]: [ + ...(acc[newConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [newConnector.fields]), ], }; }, {} as Record>); @@ -137,9 +158,7 @@ export const getPushedInfo = ( const hasDataToPushForConnector = (connectorId: string): boolean => { const caseUserActionsReversed = [...caseUserActions].reverse(); const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => - mua.action === 'push-to-service' && - getExternalService(`${mua.newValue}`)?.connectorId === connectorId + (mua) => mua.action === 'push-to-service' && mua.newValConnectorId === connectorId ); if (lastPushOfConnectorReversedIndex === -1) { @@ -190,7 +209,7 @@ export const getPushedInfo = ( return acc; } - const externalService = getExternalService(`${cua.newValue}`); + const externalService = getExternalService(cua.newValConnectorId, cua.newValue); if (externalService === null) { return acc; } diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index de67b1cfbd6f..b0cc0c72fee7 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -36,14 +36,6 @@ import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; -export const parseString = (params: string) => { - try { - return JSON.parse(params); - } catch { - return null; - } -}; - export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { if (isArray(value)) { diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index 507405d58cef..b84a6bd84c43 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -106,7 +106,7 @@ async function getSubCase({ caseId, subCaseId: newSubCase.id, fields: ['status', 'sub_case'], - newValue: JSON.stringify({ status: newSubCase.attributes.status }), + newValue: { status: newSubCase.attributes.status }, owner: newSubCase.attributes.owner, }), ], @@ -220,7 +220,7 @@ const addGeneratedAlerts = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], @@ -408,7 +408,7 @@ export const addComment = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index 9816efd9a845..b5e9e6c37235 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -17,6 +17,7 @@ import { SUB_CASE_SAVED_OBJECT, CaseResponse, CommentPatchRequest, + CommentRequest, } from '../../../common'; import { AttachmentService, CasesService } from '../../services'; import { CasesClientArgs } from '..'; @@ -193,12 +194,12 @@ export async function update( subCaseId: subCaseID, commentId: updatedComment.id, fields: ['comment'], - newValue: JSON.stringify(queryRestAttributes), - oldValue: JSON.stringify( + // casting because typescript is complaining that it's not a Record even though it is + newValue: queryRestAttributes as CommentRequest, + oldValue: // We are interested only in ContextBasicRt attributes // myComment.attribute contains also CommentAttributesBasicRt attributes - pick(Object.keys(queryRestAttributes), myComment.attributes) - ), + pick(Object.keys(queryRestAttributes), myComment.attributes), owner: myComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 887990fef893..488bc523f779 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -106,7 +106,7 @@ export const create = async ( actionBy: { username, full_name, email }, caseId: newCase.id, fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', OWNER_FIELD], - newValue: JSON.stringify(query), + newValue: query, owner: newCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 80a687a0e72f..4333535f17a2 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -168,7 +168,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P 'settings', OWNER_FIELD, 'comment', - ...(ENABLE_CASE_CONNECTOR ? ['sub_case'] : []), + ...(ENABLE_CASE_CONNECTOR ? ['sub_case' as const] : []), ], owner: caseInfo.attributes.owner, }) diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 313d6cd12a6d..22520cea1101 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -231,8 +231,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"id":"456","name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -248,7 +250,9 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', + old_val_connector_id: null, old_value: null, action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -265,6 +269,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: '{"type":"alert","alertId":"alert-id-1","index":".siem-signals-default-000008"}', + new_val_connector_id: null, + old_val_connector_id: null, old_value: null, action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -282,6 +288,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"type":"alert","alertId":"alert-id-2","index":".siem-signals-default-000008"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', @@ -297,8 +305,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -315,6 +325,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"comment":"a comment!","type":"user"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 3048cf01bb3b..1b090a653546 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,7 @@ export const push = async ( actionBy: { username, full_name, email }, caseId, fields: ['pushed'], - newValue: JSON.stringify(externalService), + newValue: externalService, owner: myCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index d7c45d3e1e9a..315e9966d347 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -799,8 +799,10 @@ describe('utils', () => { username: 'elastic', }, new_value: - // The connector id is 123 - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"123","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + // The connector id is 123 + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '123', + old_val_connector_id: null, old_value: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 359ad4b41ead..f5cf2fe4b3f5 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,6 +20,8 @@ import { CommentRequestUserType, CommentRequestAlertType, CommentRequestActionsType, + CaseUserActionResponse, + isPush, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -55,22 +57,36 @@ export const getLatestPushInfo = ( userActions: CaseUserActionsResponse ): { index: number; pushedInfo: CaseFullExternalService } | null => { for (const [index, action] of [...userActions].reverse().entries()) { - if (action.action === 'push-to-service' && action.new_value) + if ( + isPush(action.action, action.action_field) && + isValidNewValue(action) && + connectorId === action.new_val_connector_id + ) { try { const pushedInfo = JSON.parse(action.new_value); - if (pushedInfo.connector_id === connectorId) { - // We returned the index of the element in the userActions array. - // As we traverse the userActions in reverse we need to calculate the index of a normal traversal - return { index: userActions.length - index - 1, pushedInfo }; - } + // We returned the index of the element in the userActions array. + // As we traverse the userActions in reverse we need to calculate the index of a normal traversal + return { + index: userActions.length - index - 1, + pushedInfo: { ...pushedInfo, connector_id: connectorId }, + }; } catch (e) { - // Silence JSON parse errors + // ignore parse failures and check the next user action } + } } return null; }; +type NonNullNewValueAction = Omit & { + new_value: string; + new_val_connector_id: string; +}; + +const isValidNewValue = (userAction: CaseUserActionResponse): userAction is NonNullNewValueAction => + userAction.new_val_connector_id != null && userAction.new_value != null; + const getCommentContent = (comment: CommentResponse): string => { if (comment.type === CommentType.user) { return comment.comment; diff --git a/x-pack/plugins/cases/server/client/user_actions/get.test.ts b/x-pack/plugins/cases/server/client/user_actions/get.test.ts new file mode 100644 index 000000000000..302e069cde4d --- /dev/null +++ b/x-pack/plugins/cases/server/client/user_actions/get.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionResponse, SUB_CASE_SAVED_OBJECT } from '../../../common'; +import { SUB_CASE_REF_NAME } from '../../common'; +import { extractAttributesWithoutSubCases } from './get'; + +describe('get', () => { + describe('extractAttributesWithoutSubCases', () => { + it('returns an empty array when given an empty array', () => { + expect( + extractAttributesWithoutSubCases({ ...getFindResponseFields(), saved_objects: [] }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference with other references', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('keeps saved objects that do not have a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '1' }]); + }); + + it('filters multiple saved objects correctly', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '2' } as unknown as CaseUserActionResponse, + }, + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '2' }]); + }); + }); +}); + +const getFindResponseFields = () => ({ page: 1, per_page: 1, total: 0 }); diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 2a6608014c80..660cf1b6a336 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -5,14 +5,14 @@ * 2.0. */ +import { SavedObjectReference, SavedObjectsFindResponse } from 'kibana/server'; import { - CASE_COMMENT_SAVED_OBJECT, - CASE_SAVED_OBJECT, CaseUserActionsResponse, CaseUserActionsResponseRt, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, } from '../../../common'; -import { createCaseError, checkEnabledCaseConnectorOrThrow } from '../../common'; +import { createCaseError, checkEnabledCaseConnectorOrThrow, SUB_CASE_REF_NAME } from '../../common'; import { CasesClientArgs } from '..'; import { Operations } from '../../authorization'; import { UserActionGet } from './client'; @@ -40,23 +40,12 @@ export const get = async ( operation: Operations.getUserActions, }); - return CaseUserActionsResponseRt.encode( - userActions.saved_objects.reduce((acc, ua) => { - if (subCaseId == null && ua.references.some((uar) => uar.type === SUB_CASE_SAVED_OBJECT)) { - return acc; - } - return [ - ...acc, - { - ...ua.attributes, - action_id: ua.id, - case_id: ua.references.find((r) => r.type === CASE_SAVED_OBJECT)?.id ?? '', - comment_id: ua.references.find((r) => r.type === CASE_COMMENT_SAVED_OBJECT)?.id ?? null, - sub_case_id: ua.references.find((r) => r.type === SUB_CASE_SAVED_OBJECT)?.id ?? '', - }, - ]; - }, []) - ); + const resultsToEncode = + subCaseId == null + ? extractAttributesWithoutSubCases(userActions) + : extractAttributes(userActions); + + return CaseUserActionsResponseRt.encode(resultsToEncode); } catch (error) { throw createCaseError({ message: `Failed to retrieve user actions case id: ${caseId} sub case id: ${subCaseId}: ${error}`, @@ -65,3 +54,21 @@ export const get = async ( }); } }; + +export function extractAttributesWithoutSubCases( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + // exclude user actions relating to sub cases from the results + const hasSubCaseReference = (references: SavedObjectReference[]) => + references.find((ref) => ref.type === SUB_CASE_SAVED_OBJECT && ref.name === SUB_CASE_REF_NAME); + + return userActions.saved_objects + .filter((so) => !hasSubCaseReference(so.references)) + .map((so) => so.attributes); +} + +function extractAttributes( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + return userActions.saved_objects.map((so) => so.attributes); +} diff --git a/x-pack/plugins/cases/server/common/constants.ts b/x-pack/plugins/cases/server/common/constants.ts index 1f6af310d6ec..eba0a64a5c0b 100644 --- a/x-pack/plugins/cases/server/common/constants.ts +++ b/x-pack/plugins/cases/server/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common'; + /** * The name of the saved object reference indicating the action connector ID. This is stored in the Saved Object reference * field's name property. @@ -15,3 +17,30 @@ export const CONNECTOR_ID_REFERENCE_NAME = 'connectorId'; * The name of the saved object reference indicating the action connector ID that was used to push a case. */ export const PUSH_CONNECTOR_ID_REFERENCE_NAME = 'pushConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for + * adding a connector, or updating the existing connector for a user action's old_value field. + */ +export const USER_ACTION_OLD_ID_REF_NAME = 'oldConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for pushing a case, + * for a user action's old_value field. + */ +export const USER_ACTION_OLD_PUSH_ID_REF_NAME = 'oldPushConnectorId'; + +/** + * The name of the saved object reference indicating the caseId reference + */ +export const CASE_REF_NAME = `associated-${CASE_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the commentId reference + */ +export const COMMENT_REF_NAME = `associated-${CASE_COMMENT_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the subCaseId reference + */ +export const SUB_CASE_REF_NAME = `associated-${SUB_CASE_SAVED_OBJECT}`; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index bca12a86a544..9020f65ae352 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -30,322 +30,324 @@ const create_7_14_0_case = ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector.id is none', () => { - const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); +describe('case migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector.id is none', () => { + const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the connector is undefined', () => { - const caseSavedObject = create_7_14_0_case(); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + it('does not create a reference when the connector is undefined', () => { + const caseSavedObject = create_7_14_0_case(); - it('sets the connector to the default none connector if the connector.id is undefined', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - fields: null, - name: ConnectorTypes.jira, - type: ConnectorTypes.jira, - } as ESCaseConnectorWithId, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the external_service is null', () => { - const caseSavedObject = create_7_14_0_case({ externalService: null }); + it('sets the connector to the default none connector if the connector.id is undefined', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + fields: null, + name: ConnectorTypes.jira, + type: ConnectorTypes.jira, + } as ESCaseConnectorWithId, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is null', () => { + const caseSavedObject = create_7_14_0_case({ externalService: null }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service is undefined and sets external_service to null', () => { - const caseSavedObject = create_7_14_0_case(); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is undefined and sets external_service to null', () => { + const caseSavedObject = create_7_14_0_case(); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service.connector_id is none', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: createExternalService({ connector_id: noneConnectorId }), + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('preserves the existing references when migrating', () => { - const caseSavedObject = { - ...create_7_14_0_case(), - references: [{ id: '1', name: 'awesome', type: 'hello' }], - }; + it('does not create a reference when the external_service.connector_id is none', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: createExternalService({ connector_id: noneConnectorId }), + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "1", - "name": "awesome", - "type": "hello", - }, - ] - `); - }); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); - it('creates a connector reference and removes the connector.id field', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + it('preserves the existing references when migrating', () => { + const caseSavedObject = { + ...create_7_14_0_case(), + references: [{ id: '1', name: 'awesome', type: 'hello' }], + }; + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "awesome", + "type": "hello", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + it('creates a connector reference and removes the connector.id field', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, - ] - `); - }); + }); - it('creates a push connector reference and removes the connector_id field', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); - it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: null, - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', + it('creates a push connector reference and removes the connector_id field', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: null, + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - }); + }); - it('migrates both connector and external_service when provided', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(2); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('migrates both connector and external_service when provided', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(2); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index bffd4171270e..80f02fa3bf6a 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -14,7 +14,11 @@ import { } from '../../../../../../src/core/server'; import { ESConnectorFields } from '../../services'; import { ConnectorTypes, CaseType } from '../../../common'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; +import { + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedCaseConnector { connector_id: string; @@ -50,11 +54,13 @@ export const caseConnectorIdMigration = ( // removing the id field since it will be stored in the references instead const { connector, external_service, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { transformedPushConnector, references: pushConnectorReferences } = - transformPushConnectorIdToReference(external_service); + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, external_service); const { references = [] } = doc; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index 4467b499817a..9ae0285598db 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -40,87 +40,89 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector ID is none', () => { - const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); +describe('configuration migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector ID is none', () => { + const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('creates a reference using the connector id', () => { - const configureSavedObject = create_7_14_0_configSchema({ - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('creates a reference using the connector id', () => { + const configureSavedObject = create_7_14_0_configSchema({ + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }); - expect(migratedConnector.references).toEqual([ - { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, - ]); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - it('returns the other attributes and default connector when the connector is undefined', () => { - const configureSavedObject = create_7_14_0_configSchema(); + expect(migratedConnector.references).toEqual([ + { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, + ]); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('returns the other attributes and default connector when the connector is undefined', () => { + const configureSavedObject = create_7_14_0_configSchema(); - expect(migratedConnector).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "closure_type": "close-by-pushing", - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "owner": "securitySolution", - "updated_at": "2020-04-09T09:43:51.778Z", - "updated_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "closure_type": "close-by-pushing", + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + "owner": "securitySolution", + "updated_at": "2020-04-09T09:43:51.778Z", + "updated_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, }, - }, - "id": "1", - "references": Array [], - "type": "cases-configure", - } - `); + "id": "1", + "references": Array [], + "type": "cases-configure", + } + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index 527d40fca2e3..f9937253e0d2 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -13,7 +13,8 @@ import { } from '../../../../../../src/core/server'; import { ConnectorTypes } from '../../../common'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { transformConnectorIdToReference } from './utils'; +import { transformConnectorIdToReference } from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedConfigureConnector { connector_id: string; @@ -34,8 +35,10 @@ export const configureConnectorIdMigration = ( ): SavedObjectSanitizedDoc => { // removing the id field since it will be stored in the references instead const { connector, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { references = [] } = doc; return { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts index a445131073d1..a4f50fbfcde5 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts @@ -5,24 +5,17 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ - import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, } from '../../../../../../src/core/server'; -import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; export { caseMigrations } from './cases'; export { configureMigrations } from './configuration'; +export { userActionsMigrations } from './user_actions'; export { createCommentsMigrations, CreateCommentsMigrationsDeps } from './comments'; -interface UserActions { - action_field: string[]; - new_value: string; - old_value: string; -} - export interface SanitizedCaseOwner { owner: string; } @@ -38,52 +31,6 @@ export const addOwnerToSO = >( references: doc.references || [], }); -export const userActionsMigrations = { - '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { - const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; - - if ( - action_field == null || - !Array.isArray(action_field) || - action_field[0] !== 'connector_id' - ) { - return { ...doc, references: doc.references || [] }; - } - - return { - ...doc, - attributes: { - ...restAttributes, - action_field: ['connector'], - new_value: - new_value != null - ? JSON.stringify({ - id: new_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : new_value, - old_value: - old_value != null - ? JSON.stringify({ - id: old_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : old_value, - }, - references: doc.references || [], - }; - }, - '7.14.0': ( - doc: SavedObjectUnsanitizedDoc> - ): SavedObjectSanitizedDoc => { - return addOwnerToSO(doc); - }, -}; - export const connectorMappingsMigrations = { '7.14.0': ( doc: SavedObjectUnsanitizedDoc> diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts new file mode 100644 index 000000000000..e71c8db0db69 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts @@ -0,0 +1,562 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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/naming-convention */ + +import { SavedObjectMigrationContext, SavedObjectSanitizedDoc } from 'kibana/server'; +import { migrationMocks } from 'src/core/server/mocks'; +import { CaseUserActionAttributes, CASE_USER_ACTION_SAVED_OBJECT } from '../../../common'; +import { + createConnectorObject, + createExternalService, + createJiraConnector, +} from '../../services/test_utils'; +import { userActionsConnectorIdMigration } from './user_actions'; + +const create_7_14_0_userAction = ( + params: { + action?: string; + action_field?: string[]; + new_value?: string | null | object; + old_value?: string | null | object; + } = {} +) => { + const { new_value, old_value, ...restParams } = params; + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '1', + attributes: { + ...restParams, + new_value: new_value && typeof new_value === 'object' ? JSON.stringify(new_value) : new_value, + old_value: old_value && typeof old_value === 'object' ? JSON.stringify(old_value) : old_value, + }, + }; +}; + +describe('user action migrations', () => { + describe('7.15.0 connector ID migration', () => { + describe('userActionsConnectorIdMigration', () => { + let context: jest.Mocked; + + beforeEach(() => { + context = migrationMocks.createContext(); + }); + + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the external_service connector_id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['invalid field'], + new_value: 'hello', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when it new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction.attributes.new_value).toEqual('{a'); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "pushed", + ], + "new_value": "{a", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId).not.toHaveProperty('id'); + expect(parsedOldConnectorId).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: '{}', + old_value: '{b', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "connector", + ], + "new_value": "{}", + "old_value": "{b", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: '{b', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId.connector).not.toHaveProperty('id'); + expect(parsedOldConnectorId.connector).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "connector", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts new file mode 100644 index 000000000000..ed6b57ef647f --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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/naming-convention */ + +import { addOwnerToSO, SanitizedCaseOwner } from '.'; +import { + SavedObjectUnsanitizedDoc, + SavedObjectSanitizedDoc, + SavedObjectMigrationContext, + LogMeta, +} from '../../../../../../src/core/server'; +import { ConnectorTypes, isCreateConnector, isPush, isUpdateConnector } from '../../../common'; + +import { extractConnectorIdFromJson } from '../../services/user_actions/transform'; +import { UserActionFieldType } from '../../services/user_actions/types'; + +interface UserActions { + action_field: string[]; + new_value: string; + old_value: string; +} + +interface UserActionUnmigratedConnectorDocument { + action?: string; + action_field?: string[]; + new_value?: string | null; + old_value?: string | null; +} + +interface UserActionLogMeta extends LogMeta { + migrations: { userAction: { id: string } }; +} + +export function userActionsConnectorIdMigration( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext +): SavedObjectSanitizedDoc { + const originalDocWithReferences = { ...doc, references: doc.references ?? [] }; + + if (!isConnectorUserAction(doc.attributes.action, doc.attributes.action_field)) { + return originalDocWithReferences; + } + + try { + return formatDocumentWithConnectorReferences(doc); + } catch (error) { + logError(doc.id, context, error); + + return originalDocWithReferences; + } +} + +function isConnectorUserAction(action?: string, actionFields?: string[]): boolean { + return ( + isCreateConnector(action, actionFields) || + isUpdateConnector(action, actionFields) || + isPush(action, actionFields) + ); +} + +function formatDocumentWithConnectorReferences( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc { + const { new_value, old_value, action, action_field, ...restAttributes } = doc.attributes; + const { references = [] } = doc; + + const { transformedActionDetails: transformedNewValue, references: newValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: new_value, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: old_value, + fieldType: UserActionFieldType.Old, + }); + + return { + ...doc, + attributes: { + ...restAttributes, + action, + action_field, + new_value: transformedNewValue, + old_value: transformedOldValue, + }, + references: [...references, ...newValueConnectorRefs, ...oldValueConnectorRefs], + }; +} + +function logError(id: string, context: SavedObjectMigrationContext, error: Error) { + context.log.error( + `Failed to migrate user action connector doc id: ${id} version: ${context.migrationVersion} error: ${error.message}`, + { + migrations: { + userAction: { + id, + }, + }, + } + ); +} + +export const userActionsMigrations = { + '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { + const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; + + if ( + action_field == null || + !Array.isArray(action_field) || + action_field[0] !== 'connector_id' + ) { + return { ...doc, references: doc.references || [] }; + } + + return { + ...doc, + attributes: { + ...restAttributes, + action_field: ['connector'], + new_value: + new_value != null + ? JSON.stringify({ + id: new_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : new_value, + old_value: + old_value != null + ? JSON.stringify({ + id: old_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : old_value, + }, + references: doc.references || [], + }; + }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc> + ): SavedObjectSanitizedDoc => { + return addOwnerToSO(doc); + }, + '7.16.0': userActionsConnectorIdMigration, +}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts deleted file mode 100644 index f591bef6b323..000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts +++ /dev/null @@ -1,229 +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 { noneConnectorId } from '../../../common'; -import { createExternalService, createJiraConnector } from '../../services/test_utils'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; - -describe('migration utils', () => { - describe('transformConnectorIdToReference', () => { - it('returns the default none connector when the connector is undefined', () => { - expect(transformConnectorIdToReference().transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns an empty array of references when the connector is undefined', () => { - expect(transformConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .references.length - ).toBe(0); - }); - - it('returns a jira connector', () => { - const transformedFields = transformConnectorIdToReference(createJiraConnector()); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('transformPushConnectorIdToReference', () => { - it('sets external_service to null when it is undefined', () => { - expect(transformPushConnectorIdToReference().transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('sets external_service to null when it is null', () => { - expect(transformPushConnectorIdToReference(null).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is null', () => { - expect(transformPushConnectorIdToReference({ connector_id: null }).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is none', () => { - const otherFields = { otherField: 'hi' }; - - expect( - transformPushConnectorIdToReference({ ...otherFields, connector_id: noneConnectorId }) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "otherField": "hi", - }, - } - `); - }); - - it('returns an empty array of references when the external_service is undefined', () => { - expect(transformPushConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the external_service is null', () => { - expect(transformPushConnectorIdToReference(null).references.length).toBe(0); - }); - - it('returns an empty array of references when the connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is null', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector', () => { - expect( - transformPushConnectorIdToReference({ connector_id: noneConnectorId }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { - expect( - transformPushConnectorIdToReference({ - ...createExternalService(), - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns the external_service connector', () => { - const transformedFields = transformPushConnectorIdToReference(createExternalService()); - expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts deleted file mode 100644 index 0100a04cde67..000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts +++ /dev/null @@ -1,73 +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. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { noneConnectorId } from '../../../common'; -import { SavedObjectReference } from '../../../../../../src/core/server'; -import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { - getNoneCaseConnector, - CONNECTOR_ID_REFERENCE_NAME, - PUSH_CONNECTOR_ID_REFERENCE_NAME, -} from '../../common'; - -export const transformConnectorIdToReference = (connector?: { - id?: string; -}): { transformedConnector: Record; references: SavedObjectReference[] } => { - const { id: connectorId, ...restConnector } = connector ?? {}; - - const references = createConnectorReference( - connectorId, - ACTION_SAVED_OBJECT_TYPE, - CONNECTOR_ID_REFERENCE_NAME - ); - - const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); - const connectorFieldsToReturn = - connector && references.length > 0 ? restConnector : restNoneConnector; - - return { - transformedConnector: { - connector: connectorFieldsToReturn, - }, - references, - }; -}; - -const createConnectorReference = ( - id: string | null | undefined, - type: string, - name: string -): SavedObjectReference[] => { - return id && id !== noneConnectorId - ? [ - { - id, - type, - name, - }, - ] - : []; -}; - -export const transformPushConnectorIdToReference = ( - external_service?: { connector_id?: string | null } | null -): { transformedPushConnector: Record; references: SavedObjectReference[] } => { - const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; - - const references = createConnectorReference( - pushConnectorId, - ACTION_SAVED_OBJECT_TYPE, - PUSH_CONNECTOR_ID_REFERENCE_NAME - ); - - return { - transformedPushConnector: { external_service: external_service ? restExternalService : null }, - references, - }; -}; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 18f4ff867cfa..8c71abe5bff4 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -40,6 +40,7 @@ import { createSavedObjectReferences, createCaseSavedObjectResponse, basicCaseFields, + createSOFindResponse, } from '../test_utils'; import { ESCaseAttributes } from './types'; @@ -87,13 +88,6 @@ const createFindSO = ( score: 0, }); -const createSOFindResponse = (savedObjects: Array>) => ({ - saved_objects: savedObjects, - total: savedObjects.length, - per_page: savedObjects.length, - page: 1, -}); - const createCaseUpdateParams = ( connector?: CaseConnector, externalService?: CaseFullExternalService diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index b712ea07f9c7..07743eda6121 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsFindResult } from 'kibana/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common'; import { @@ -54,7 +54,7 @@ export const createESJiraConnector = ( { key: 'parent', value: '2' }, ], type: ConnectorTypes.jira, - ...(overrides && { ...overrides }), + ...overrides, }; }; @@ -94,7 +94,7 @@ export const createExternalService = ( email: 'testemail@elastic.co', username: 'elastic', }, - ...(overrides && { ...overrides }), + ...overrides, }); export const basicCaseFields = { @@ -198,3 +198,14 @@ export const createSavedObjectReferences = ({ ] : []), ]; + +export const createConnectorObject = (overrides?: Partial) => ({ + connector: { ...createJiraConnector(), ...overrides }, +}); + +export const createSOFindResponse = (savedObjects: Array>) => ({ + saved_objects: savedObjects, + total: savedObjects.length, + per_page: savedObjects.length, + page: 1, +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts new file mode 100644 index 000000000000..7bcbaf58d0f6 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts @@ -0,0 +1,332 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserActionField } from '../../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { buildCaseUserActionItem } from './helpers'; + +const defaultFields = () => ({ + actionAt: 'now', + actionBy: { + email: 'a', + full_name: 'j', + username: '1', + }, + caseId: '300', + owner: 'securitySolution', +}); + +describe('user action helpers', () => { + describe('buildCaseUserActionItem', () => { + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + }); + + const parsedExternalService = JSON.parse(userAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue: createExternalService({ connector_id: '5' }), + }); + + const parsedNewExternalService = JSON.parse(userAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(userAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + }); + + expect(userAction.attributes.old_value).toBeNull(); + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue: { ...createJiraConnector(), id: '5' }, + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + oldValue: createConnectorObject({ id: '5' }), + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['invalid action'] as unknown as UserActionField, + newValue: 'new json value' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index 223e731aa8d9..e91b69f0995b 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsUpdateResponse } from 'kibana/server'; import { get, isPlainObject, isString } from 'lodash'; import deepEqual from 'fast-deep-equal'; @@ -23,8 +23,68 @@ import { } from '../../../common'; import { isTwoArraysDifference } from '../../client/utils'; import { UserActionItem } from '.'; +import { extractConnectorId } from './transform'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -export const transformNewUserAction = ({ +interface BuildCaseUserActionParams { + action: UserAction; + actionAt: string; + actionBy: User; + caseId: string; + owner: string; + fields: UserActionField; + newValue?: Record | string | null; + oldValue?: Record | string | null; + subCaseId?: string; +} + +export const buildCaseUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCaseUserActionParams): UserActionItem => { + const { transformedActionDetails: transformedNewValue, references: newValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: newValue, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: oldValue, + fieldType: UserActionFieldType.Old, + }); + + return { + attributes: transformNewUserAction({ + actionField: fields, + action, + actionAt, + owner, + ...actionBy, + newValue: transformedNewValue, + oldValue: transformedOldValue, + }), + references: [ + ...createCaseReferences(caseId, subCaseId), + ...newValueReferences, + ...oldValueReferences, + ], + }; +}; + +const transformNewUserAction = ({ actionField, action, actionAt, @@ -55,103 +115,43 @@ export const transformNewUserAction = ({ owner, }); -interface BuildCaseUserAction { - action: UserAction; - actionAt: string; - actionBy: User; - caseId: string; - owner: string; - fields: UserActionField | unknown[]; - newValue?: string | unknown; - oldValue?: string | unknown; - subCaseId?: string; -} +const createCaseReferences = (caseId: string, subCaseId?: string): SavedObjectReference[] => [ + { + type: CASE_SAVED_OBJECT, + name: CASE_REF_NAME, + id: caseId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + name: SUB_CASE_REF_NAME, + id: subCaseId, + }, + ] + : []), +]; -interface BuildCommentUserActionItem extends BuildCaseUserAction { +interface BuildCommentUserActionItem extends BuildCaseUserActionParams { commentId: string; } -export const buildCommentUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - commentId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCommentUserActionItem): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - { - type: CASE_COMMENT_SAVED_OBJECT, - name: `associated-${CASE_COMMENT_SAVED_OBJECT}`, - id: commentId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - id: subCaseId, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - }, - ] - : []), - ], -}); +export const buildCommentUserActionItem = (params: BuildCommentUserActionItem): UserActionItem => { + const { commentId } = params; + const { attributes, references } = buildCaseUserActionItem(params); -export const buildCaseUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCaseUserAction): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - id: subCaseId, - }, - ] - : []), - ], -}); + return { + attributes, + references: [ + ...references, + { + type: CASE_COMMENT_SAVED_OBJECT, + name: COMMENT_REF_NAME, + id: commentId, + }, + ], + }; +}; const userActionFieldsAllowed: UserActionField = [ 'comment', @@ -278,8 +278,8 @@ const buildGenericCaseUserActions = ({ caseId, subCaseId, fields: [field], - newValue: JSON.stringify(updatedValue), - oldValue: JSON.stringify(origValue), + newValue: updatedValue, + oldValue: origValue, owner: originalItem.attributes.owner, }), ]; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts new file mode 100644 index 000000000000..c4a350f4ac01 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/index.test.ts @@ -0,0 +1,557 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObject, SavedObjectsFindResult } from 'kibana/server'; +import { transformFindResponseToExternalModel, UserActionItem } from '.'; +import { + CaseUserActionAttributes, + CASE_USER_ACTION_SAVED_OBJECT, + UserAction, + UserActionField, +} from '../../../common'; + +import { + createConnectorObject, + createExternalService, + createJiraConnector, + createSOFindResponse, +} from '../test_utils'; +import { buildCaseUserActionItem, buildCommentUserActionItem } from './helpers'; + +const createConnectorUserAction = ( + subCaseId?: string, + overrides?: Partial +): SavedObject => { + return { + ...createUserActionSO({ + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const updateConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const pushConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const createUserActionFindSO = ( + userAction: SavedObject +): SavedObjectsFindResult => ({ + ...userAction, + score: 0, +}); + +const createUserActionSO = ({ + action, + fields, + subCaseId, + newValue, + oldValue, + attributesOverrides, + commentId, +}: { + action: UserAction; + fields: UserActionField; + subCaseId?: string; + newValue?: string | null | Record; + oldValue?: string | null | Record; + attributesOverrides?: Partial; + commentId?: string; +}): SavedObject => { + const defaultParams = { + action, + actionAt: 'abc', + actionBy: { + email: 'a', + username: 'b', + full_name: 'abc', + }, + caseId: '1', + subCaseId, + fields, + newValue, + oldValue, + owner: 'securitySolution', + }; + + let userAction: UserActionItem; + + if (commentId) { + userAction = buildCommentUserActionItem({ + commentId, + ...defaultParams, + }); + } else { + userAction = buildCaseUserActionItem(defaultParams); + } + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '100', + attributes: { + ...userAction.attributes, + ...(attributesOverrides && { ...attributesOverrides }), + }, + references: userAction.references, + }; +}; + +describe('CaseUserActionService', () => { + describe('transformFindResponseToExternalModel', () => { + it('does not populate the ids when the response is an empty array', () => { + expect(transformFindResponseToExternalModel(createSOFindResponse([]))).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 0, + "saved_objects": Array [], + "total": 0, + } + `); + }); + + it('preserves the saved object fields and attributes when inject the ids', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(createConnectorUserAction())]) + ); + + expect(transformed).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 1, + "saved_objects": Array [ + Object { + "attributes": Object { + "action": "create", + "action_at": "abc", + "action_by": Object { + "email": "a", + "full_name": "abc", + "username": "b", + }, + "action_field": Array [ + "connector", + ], + "action_id": "100", + "case_id": "1", + "comment_id": null, + "new_val_connector_id": "1", + "new_value": "{\\"connector\\":{\\"name\\":\\".jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"bug\\",\\"priority\\":\\"high\\",\\"parent\\":\\"2\\"}}}", + "old_val_connector_id": null, + "old_value": null, + "owner": "securitySolution", + "sub_case_id": "", + }, + "id": "100", + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ], + "score": 0, + "type": "cases-user-actions", + }, + ], + "total": 1, + } + `); + }); + + it('populates the new_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(createConnectorUserAction()), + createUserActionFindSO(createConnectorUserAction()), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }) + ), + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject({ id: '10' }), + }) + ), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.old_val_connector_id).toEqual('10'); + }); + + describe('reference ids', () => { + it('sets case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createConnectorUserAction(), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual(''); + }); + + it('sets comment_id to null when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], commentId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets sub_case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets case_id correctly when it finds the reference', () => { + const userAction = createConnectorUserAction(); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual('1'); + }); + + it('sets comment_id correctly when it finds the reference', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + commentId: '5', + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toEqual('5'); + }); + + it('sets sub_case_id correctly when it finds the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.sub_case_id).toEqual('5'); + }); + + it('sets action_id correctly to the saved object id', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.action_id).toEqual('100'); + }); + }); + + describe('create connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = createConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('update connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...updateConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...updateConnectorUserAction({ oldValue: createJiraConnector() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = updateConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('push connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...pushConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...pushConnectorUserAction({ oldValue: createExternalService() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = pushConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('100'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('100'); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index b70244816555..4f158862e3d6 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { Logger, SavedObjectReference } from 'kibana/server'; +import { + Logger, + SavedObjectReference, + SavedObjectsFindResponse, + SavedObjectsFindResult, +} from 'kibana/server'; import { CASE_SAVED_OBJECT, @@ -13,8 +18,17 @@ import { CaseUserActionAttributes, MAX_DOCS_PER_PAGE, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, + CASE_COMMENT_SAVED_OBJECT, + isCreateConnector, + isPush, + isUpdateConnector, } from '../../../common'; import { ClientArgs } from '..'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; +import { ConnectorIdReferenceName, PushConnectorIdReferenceName } from './transform'; +import { findConnectorIdReference } from '../transform'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -33,12 +47,16 @@ interface PostCaseUserActionArgs extends ClientArgs { export class CaseUserActionService { constructor(private readonly log: Logger) {} - public async getAll({ unsecuredSavedObjectsClient, caseId, subCaseId }: GetCaseUserActionArgs) { + public async getAll({ + unsecuredSavedObjectsClient, + caseId, + subCaseId, + }: GetCaseUserActionArgs): Promise> { try { const id = subCaseId ?? caseId; const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT; - return await unsecuredSavedObjectsClient.find({ + const userActions = await unsecuredSavedObjectsClient.find({ type: CASE_USER_ACTION_SAVED_OBJECT, hasReference: { type, id }, page: 1, @@ -46,17 +64,22 @@ export class CaseUserActionService { sortField: 'action_at', sortOrder: 'asc', }); + + return transformFindResponseToExternalModel(userActions); } catch (error) { this.log.error(`Error on GET case user action case id: ${caseId}: ${error}`); throw error; } } - public async bulkCreate({ unsecuredSavedObjectsClient, actions }: PostCaseUserActionArgs) { + public async bulkCreate({ + unsecuredSavedObjectsClient, + actions, + }: PostCaseUserActionArgs): Promise { try { this.log.debug(`Attempting to POST a new case user action`); - return await unsecuredSavedObjectsClient.bulkCreate( + await unsecuredSavedObjectsClient.bulkCreate( actions.map((action) => ({ type: CASE_USER_ACTION_SAVED_OBJECT, ...action })) ); } catch (error) { @@ -65,3 +88,71 @@ export class CaseUserActionService { } } } + +export function transformFindResponseToExternalModel( + userActions: SavedObjectsFindResponse +): SavedObjectsFindResponse { + return { + ...userActions, + saved_objects: userActions.saved_objects.map((so) => ({ + ...so, + ...transformToExternalModel(so), + })), + }; +} + +function transformToExternalModel( + userAction: SavedObjectsFindResult +): SavedObjectsFindResult { + const { references } = userAction; + + const newValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.New, userAction); + const oldValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.Old, userAction); + + const caseId = findReferenceId(CASE_REF_NAME, CASE_SAVED_OBJECT, references) ?? ''; + const commentId = + findReferenceId(COMMENT_REF_NAME, CASE_COMMENT_SAVED_OBJECT, references) ?? null; + const subCaseId = findReferenceId(SUB_CASE_REF_NAME, SUB_CASE_SAVED_OBJECT, references) ?? ''; + + return { + ...userAction, + attributes: { + ...userAction.attributes, + action_id: userAction.id, + case_id: caseId, + comment_id: commentId, + sub_case_id: subCaseId, + new_val_connector_id: newValueConnectorId, + old_val_connector_id: oldValueConnectorId, + }, + }; +} + +function getConnectorIdFromReferences( + fieldType: UserActionFieldType, + userAction: SavedObjectsFindResult +): string | null { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + attributes: { action, action_field }, + references, + } = userAction; + + if (isCreateConnector(action, action_field) || isUpdateConnector(action, action_field)) { + return findConnectorIdReference(ConnectorIdReferenceName[fieldType], references)?.id ?? null; + } else if (isPush(action, action_field)) { + return ( + findConnectorIdReference(PushConnectorIdReferenceName[fieldType], references)?.id ?? null + ); + } + + return null; +} + +function findReferenceId( + name: string, + type: string, + references: SavedObjectReference[] +): string | undefined { + return references.find((ref) => ref.name === name && ref.type === type)?.id; +} diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts new file mode 100644 index 000000000000..2d2877061709 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts @@ -0,0 +1,1246 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { noneConnectorId } from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { + extractConnectorIdHelper, + extractConnectorIdFromJson, + extractConnectorId, + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from './transform'; +import { UserActionFieldType } from './types'; + +describe('user action transform utils', () => { + describe('transformConnectorIdToReference', () => { + it('returns the default none connector when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns an empty array of references when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).references.length).toBe( + 0 + ); + }); + + it('returns an empty array of references when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .references.length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns a jira connector', () => { + const transformedFields = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a jira connector with the user action reference name', () => { + const transformedFields = transformConnectorIdToReference( + USER_ACTION_OLD_ID_REF_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('transformPushConnectorIdToReference', () => { + it('sets external_service to null when it is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('sets external_service to null when it is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: null, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is none', () => { + const otherFields = { otherField: 'hi' }; + + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...otherFields, + connector_id: noneConnectorId, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "otherField": "hi", + }, + } + `); + }); + + it('returns an empty array of references when the external_service is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the external_service is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...createExternalService(), + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns the external_service connector', () => { + const transformedFields = transformPushConnectorIdToReference( + PUSH_CONNECTOR_ID_REFERENCE_NAME, + createExternalService() + ); + expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the external_service connector with a user actions reference name', () => { + const transformedFields = transformPushConnectorIdToReference( + USER_ACTION_OLD_PUSH_ID_REF_NAME, + createExternalService() + ); + + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('extractConnectorIdHelper', () => { + it('throws an error when action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + expect(() => { + extractConnectorIdHelper({ + action: 'a', + actionFields: [], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + }).toThrow(); + }); + + describe('create action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is create and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['', 'something', 'onnector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('removes the connector.id when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson.connector).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns a reference to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the transformed connector and the description', () => { + const details = { ...createConnectorObject(), description: 'a description' }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: details, + fieldType: UserActionFieldType.Old, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + "description": "a description", + } + `); + }); + }); + + describe('update action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is update and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['', 'something', 'onnector'], + actionDetails: 5, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "5", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns the stringified json without the id when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns an old reference name to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns no references and untransformed json when actionDetails is not a valid external_service', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is push-to-service and action fields does not contain pushed', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['', 'something', 'ushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorId', () => { + it('returns null when the action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + describe('fails to extract the id', () => { + it('returns a null transformed action details when it is initially null', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: null, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + it('returns an undefined transformed action details when it is initially undefined', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeUndefined(); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action is not a valid connector', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toEqual({ a: 'hello' }); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action details is an invalid object', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: 5 as unknown as Record, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails!).toEqual('5'); + expect(references).toEqual([]); + }); + }); + + describe('create', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a reference to the old action details connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorIdFromJson', () => { + describe('fails to extract the id', () => { + it('returns no references and null transformed json when action is undefined', () => { + expect( + extractConnectorIdFromJson({ + actionFields: [], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionFields is undefined', () => { + expect( + extractConnectorIdFromJson({ action: 'a', fieldType: UserActionFieldType.New }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is undefined', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is null', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: null, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: null, + references: [], + }); + }); + + it('throws an error when actionDetails is invalid json', () => { + expect(() => + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: '{a', + fieldType: UserActionFieldType.New, + }) + ).toThrow(); + }); + }); + + describe('create action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createConnectorObject(); + + const { references } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts new file mode 100644 index 000000000000..93595374208a --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.ts @@ -0,0 +1,320 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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/naming-convention */ + +import * as rt from 'io-ts'; +import { isString } from 'lodash'; + +import { SavedObjectReference } from '../../../../../../src/core/server'; +import { + CaseAttributes, + CaseConnector, + CaseConnectorRt, + CaseExternalServiceBasicRt, + isCreateConnector, + isPush, + isUpdateConnector, + noneConnectorId, +} from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; +import { UserActionFieldType } from './types'; + +/** + * Extracts the connector id from a json encoded string and formats it as a saved object reference. This will remove + * the field it extracted the connector id from. + */ +export function extractConnectorIdFromJson({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action?: string; + actionFields?: string[]; + actionDetails?: string | null; + fieldType: UserActionFieldType; +}): { transformedActionDetails?: string | null; references: SavedObjectReference[] } { + if (!action || !actionFields || !actionDetails) { + return { transformedActionDetails: actionDetails, references: [] }; + } + + const decodedJson = JSON.parse(actionDetails); + + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails: decodedJson, + fieldType, + }); +} + +/** + * Extracts the connector id from an unencoded object and formats it as a saved object reference. + * This will remove the field it extracted the connector id from. + */ +export function extractConnectorId({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails?: Record | string | null; + fieldType: UserActionFieldType; +}): { + transformedActionDetails?: string | null; + references: SavedObjectReference[]; +} { + if (!actionDetails || isString(actionDetails)) { + // the action was null, undefined, or a regular string so just return it unmodified and not encoded + return { transformedActionDetails: actionDetails, references: [] }; + } + + try { + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, + }); + } catch (error) { + return { transformedActionDetails: encodeActionDetails(actionDetails), references: [] }; + } +} + +function encodeActionDetails(actionDetails: Record): string | null { + try { + return JSON.stringify(actionDetails); + } catch (error) { + return null; + } +} + +/** + * Internal helper function for extracting the connector id. This is only exported for usage in unit tests. + * This function handles encoding the transformed fields as a json string + */ +export function extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails: unknown; + fieldType: UserActionFieldType; +}): { transformedActionDetails: string; references: SavedObjectReference[] } { + let transformedActionDetails: unknown = actionDetails; + let referencesToReturn: SavedObjectReference[] = []; + + try { + if (isCreateCaseConnector(action, actionFields, actionDetails)) { + const { transformedActionDetails: transformedConnectorPortion, references } = + transformConnectorFromCreateAndUpdateAction(actionDetails.connector, fieldType); + + // the above call only transforms the connector portion of the action details so let's add back + // the rest of the details and we'll overwrite the connector portion when the transformed one + transformedActionDetails = { + ...actionDetails, + ...transformedConnectorPortion, + }; + referencesToReturn = references; + } else if (isUpdateCaseConnector(action, actionFields, actionDetails)) { + const { + transformedActionDetails: { connector: transformedConnector }, + references, + } = transformConnectorFromCreateAndUpdateAction(actionDetails, fieldType); + + transformedActionDetails = transformedConnector; + referencesToReturn = references; + } else if (isPushConnector(action, actionFields, actionDetails)) { + ({ transformedActionDetails, references: referencesToReturn } = + transformConnectorFromPushAction(actionDetails, fieldType)); + } + } catch (error) { + // ignore any errors, we'll just return whatever was passed in for action details in that case + } + + return { + transformedActionDetails: JSON.stringify(transformedActionDetails), + references: referencesToReturn, + }; +} + +function isCreateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is { connector: CaseConnector } { + try { + const unsafeCase = actionDetails as CaseAttributes; + + return ( + isCreateConnector(action, actionFields) && + unsafeCase.connector !== undefined && + CaseConnectorRt.is(unsafeCase.connector) + ); + } catch { + return false; + } +} + +export const ConnectorIdReferenceName: Record = { + [UserActionFieldType.New]: CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_ID_REF_NAME, +}; + +function transformConnectorFromCreateAndUpdateAction( + connector: CaseConnector, + fieldType: UserActionFieldType +): { + transformedActionDetails: { connector: unknown }; + references: SavedObjectReference[]; +} { + const { transformedConnector, references } = transformConnectorIdToReference( + ConnectorIdReferenceName[fieldType], + connector + ); + + return { + transformedActionDetails: transformedConnector, + references, + }; +} + +type ConnectorIdRefNameType = + | typeof CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_ID_REF_NAME; + +export const transformConnectorIdToReference = ( + referenceName: ConnectorIdRefNameType, + connector?: { + id?: string; + } +): { + transformedConnector: { connector: unknown }; + references: SavedObjectReference[]; +} => { + const { id: connectorId, ...restConnector } = connector ?? {}; + + const references = createConnectorReference(connectorId, ACTION_SAVED_OBJECT_TYPE, referenceName); + + const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); + const connectorFieldsToReturn = + connector && isConnectorIdValid(connectorId) ? restConnector : restNoneConnector; + + return { + transformedConnector: { + connector: connectorFieldsToReturn, + }, + references, + }; +}; + +const createConnectorReference = ( + id: string | null | undefined, + type: string, + name: string +): SavedObjectReference[] => { + return isConnectorIdValid(id) + ? [ + { + id, + type, + name, + }, + ] + : []; +}; + +const isConnectorIdValid = (id: string | null | undefined): id is string => + id != null && id !== noneConnectorId; + +function isUpdateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseConnector { + try { + return isUpdateConnector(action, actionFields) && CaseConnectorRt.is(actionDetails); + } catch { + return false; + } +} + +type CaseExternalService = rt.TypeOf; + +function isPushConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseExternalService { + try { + return isPush(action, actionFields) && CaseExternalServiceBasicRt.is(actionDetails); + } catch { + return false; + } +} + +export const PushConnectorIdReferenceName: Record = + { + [UserActionFieldType.New]: PUSH_CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_PUSH_ID_REF_NAME, + }; + +function transformConnectorFromPushAction( + externalService: CaseExternalService, + fieldType: UserActionFieldType +): { + transformedActionDetails: {} | null; + references: SavedObjectReference[]; +} { + const { transformedPushConnector, references } = transformPushConnectorIdToReference( + PushConnectorIdReferenceName[fieldType], + externalService + ); + + return { + transformedActionDetails: transformedPushConnector.external_service, + references, + }; +} + +type PushConnectorIdRefNameType = + | typeof PUSH_CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_PUSH_ID_REF_NAME; + +export const transformPushConnectorIdToReference = ( + referenceName: PushConnectorIdRefNameType, + external_service?: { connector_id?: string | null } | null +): { + transformedPushConnector: { external_service: {} | null }; + references: SavedObjectReference[]; +} => { + const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; + + const references = createConnectorReference( + pushConnectorId, + ACTION_SAVED_OBJECT_TYPE, + referenceName + ); + + return { + transformedPushConnector: { external_service: external_service ? restExternalService : null }, + references, + }; +}; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts new file mode 100644 index 000000000000..3c67535255ec --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/types.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. + */ + +/** + * Indicates whether which user action field is being parsed, the new_value or the old_value. + */ +export enum UserActionFieldType { + New = 'New', + Old = 'Old', +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 964e9135aba7..68f0ba43d889 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should create a user action when creating a case', async () => { + it('should create a user action when deleting a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); await deleteCases({ supertest, caseIDs: [postedCase.id] }); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -106,6 +106,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, old_value: null, new_value: null, + new_val_connector_id: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 63b2f2e9b90e..d7c506a6b69d 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -126,6 +126,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'update', action_by: defaultUser, new_value: CaseStatuses.closed, + new_val_connector_id: null, + old_val_connector_id: null, old_value: CaseStatuses.open, case_id: `${postedCase.id}`, comment_id: null, @@ -165,6 +167,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, new_value: CaseStatuses['in-progress'], old_value: CaseStatuses.open, + old_val_connector_id: null, + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 96709ee7c309..13408c5d309d 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -114,6 +114,8 @@ export default ({ getService }: FtrProviderContext): void => { const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; const parsedNewValue = JSON.parse(new_value!); + const { id: connectorId, ...restCaseConnector } = postedCase.connector; + expect(rest).to.eql({ action_field: [ 'description', @@ -127,6 +129,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + // the connector id will be null here because it the connector is none + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -138,7 +143,7 @@ export default ({ getService }: FtrProviderContext): void => { description: postedCase.description, title: postedCase.title, tags: postedCase.tags, - connector: postedCase.connector, + connector: restCaseConnector, settings: postedCase.settings, owner: postedCase.owner, }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index f4c31c052cdd..942293437b03 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -148,7 +148,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}","owner":"securitySolutionFixture"}`, + new_val_connector_id: null, old_value: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: `${patchedCase.comments![0].id}`, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 35ebb1a4bf7b..4cae10510d28 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -48,6 +48,15 @@ export default ({ getService }: FtrProviderContext): void => { }); it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings, owner]`, async () => { + const { id: connectorId, ...restConnector } = userActionPostResp.connector; + + const userActionNewValueNoId = { + ...userActionPostResp, + connector: { + ...restConnector, + }, + }; + const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -73,7 +82,10 @@ export default ({ getService }: FtrProviderContext): void => { ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); - expect(JSON.parse(body[0].new_value)).to.eql(userActionPostResp); + expect(body[0].old_val_connector_id).to.eql(null); + // this will be null because it is for the none connector + expect(body[0].new_val_connector_id).to.eql(null); + expect(JSON.parse(body[0].new_value)).to.eql(userActionNewValueNoId); }); it(`on close case, user action: 'update' should be called with actionFields: ['status']`, async () => { @@ -147,18 +159,19 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(2); expect(body[1].action_field).to.eql(['connector']); expect(body[1].action).to.eql('update'); + // this is null because it is the none connector + expect(body[1].old_val_connector_id).to.eql(null); expect(JSON.parse(body[1].old_value)).to.eql({ - id: 'none', name: 'none', type: '.none', fields: null, }); expect(JSON.parse(body[1].new_value)).to.eql({ - id: '123', name: 'Connector', type: '.jira', fields: { issueType: 'Task', priority: 'High', parent: null }, }); + expect(body[1].new_val_connector_id).to.eql('123'); }); it(`on update tags, user action: 'add' and 'delete' should be called with actionFields: ['tags']`, async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index b4c2dca47bf5..f9e66880c523 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -12,6 +12,10 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../../../../plugins/cases/common/constants'; import { getCaseUserActions } from '../../../../common/lib/utils'; +import { + CaseUserActionResponse, + CaseUserActionsResponse, +} from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { @@ -41,14 +45,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorUserAction.action_field.length).eql(1); expect(connectorUserAction.action_field[0]).eql('connector'); + expect(connectorUserAction.old_val_connector_id).to.eql( + 'c1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(oldValue).to.eql({ - id: 'c1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, }); + expect(connectorUserAction.new_val_connector_id).to.eql( + 'b1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(newValue).to.eql({ - id: 'b1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, @@ -77,5 +85,142 @@ export default function createGetTests({ getService }: FtrProviderContext) { } }); }); + + describe('7.13 connector id extraction', () => { + let userActions: CaseUserActionsResponse; + + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + describe('none connector case', () => { + it('removes the connector id from the case create user action and sets the ids to null', async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'aa8ac630-005e-11ec-91f1-6daf2ab59fb5', + }); + + const userAction = getUserActionById( + userActions, + 'ab43b5f0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + expect(newValDecoded.connector).not.have.property('id'); + // the connector id should be none so it should be removed + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a create user action with null new and old values', async () => { + const userAction = getUserActionById( + userActions, + 'b3094de0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + + describe('case with many user actions', () => { + before(async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'e6fa9370-005e-11ec-91f1-6daf2ab59fb5', + }); + }); + + it('removes the connector id field for a created case user action', async () => { + const userAction = getUserActionById( + userActions, + 'e7882d70-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + + expect(newValDecoded.connector).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id from the external service new value', async () => { + const userAction = getUserActionById( + userActions, + 'e9471b80-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.connector_name).to.be('a jira connector'); + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a comment user action', async () => { + const userAction = getUserActionById( + userActions, + 'efe9de50-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.comment).to.be('a comment'); + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id for an update connector action', async () => { + const userAction = getUserActionById( + userActions, + '16cd9e30-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + const oldValDecoded = JSON.parse(userAction.old_value!); + + expect(newValDecoded.name).to.be('a different jira connector'); + expect(oldValDecoded.name).to.be('a jira connector'); + + expect(newValDecoded).to.not.have.property('id'); + expect(oldValDecoded).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + }); + + it('removes the connector id from the external service new value for second push', async () => { + const userAction = getUserActionById( + userActions, + '1ea33bb0-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + + expect(newValDecoded.connector_name).to.be('a different jira connector'); + + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + }); }); } + +function getUserActionById( + userActions: CaseUserActionsResponse, + id: string +): CaseUserActionResponse | undefined { + return userActions.find((userAction) => userAction.action_id === id); +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 94fe494fc7cc..0ea66d35b63b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -275,6 +275,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'push-to-service', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + new_val_connector_id: connector.id, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -284,7 +286,6 @@ export default ({ getService }: FtrProviderContext): void => { expect(parsedNewValue).to.eql({ pushed_at: pushedCase.external_service!.pushed_at, pushed_by: defaultUser, - connector_id: connector.id, connector_name: connector.name, external_id: '123', external_title: 'INC01', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 79af6bb279a3..255a2a4ce28b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -108,8 +108,10 @@ export default ({ getService }: FtrProviderContext): void => { expect(body[1].action_field).to.eql(['pushed']); expect(body[1].action).to.eql('push-to-service'); expect(body[1].old_value).to.eql(null); + expect(body[1].old_val_connector_id).to.eql(null); + expect(body[1].new_val_connector_id).to.eql(configure.connector.id); const newValue = JSON.parse(body[1].new_value); - expect(newValue.connector_id).to.eql(configure.connector.id); + expect(newValue).to.not.have.property('connector_id'); expect(newValue.pushed_by).to.eql(defaultUser); }); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..5f73dfd89d1660ef207e6ec26ce7198f2a722616 GIT binary patch literal 2078 zcmV+(2;ui1iwFpikRD+G17u-zVJ>QOZ*BnXTuXDKI1s+)S5TY-LE>SvxgZy+IT5F>ahZaIhWE-j?DT+?5t5lioJNZyKPi$UR$bs&%<`)2h3okV(a3a zL#Oy&DHWJDNxNT|jw-rg7~^}oGPm^>`SOTBQ4}aL)$g~xMn9kcdl7$V`cR|B4O~BP zqt_0h|8$>z;4rF*UHSD`?VD}BV?wB@n?Bo~_4Cj^nGOj*-2e&YwlJ-7)stzXQl$6l zh6Wf#xgLm6fk0B05?J0t#ZnXtXtJuQ4`NG?L_HWCza&<@-~_JN!GM!|m2&)MuBmhx9q}HCvC}gmQ#_ z+fhiAP6!kL7MvE58|{)2@BjUW$f9tX#Q|M!@kbOLLhJ4>F>@&*`saF2GK$DLhc5DD z4@Y;Kz<&wxUF&#wWg|CggBy49oDxweD&-< zb~uP=O%%kqy?2D7n6?o817_4HLQi#A6T|p`gHoi&YV&;Kh!&9DU`EQUkpQ>*>^d>p zRcqse!#BjXO(=RyYKS+rwIfCl;J@9c08#t<4+mjpmkSxFvQ~^M?wf6@-Y9cWv>TKa zu`x}6x0rysjjNkcEPS!H={QAkk41(0LpobFS25I@I_w+z$nQ5dZ){!e%NyRD_2&{@ zND0~Wv59lv5O~qI{Q!pV-rF3!&^BRRE7IabxCFx4Jx^+cnue!l zJ|JK(w>}RR!qB%}icb;?Te-?HnAYk5A6wi@%Om%3Gnt)hGE~fmCjQn@F_nTKW4MH^O>YEbb(vmNHhKZMDpBhSK34k%}} z16_HGrVj!F1sK!E{n*qNV6(h{xStZ5QshfHTiojakdfp|8LD-HbS4LYl*jGQQxsHj z!VB=0BB2Tjia4bPa*30UROFOT$R9W+8NjqyjU)w({mj!hArW} zQJfE0UYX=hCb>5jcO!7gI@3y)b*7W$sRJ22D+)~AP4dz|^A+@W_Od={mc5oI0DI-E zzZ?qdvI>F<+l@p02 zaX5v>QMt(~d?G+WQ&tye2or%}J0?!wI&9AhQ0W5o2Pj~v#CY_Xo~g@Vt~W$w5tM@T z1zZ}PJ64aN9Cw?bKr~e>^_Txb<0w8>igODIIjNCXWIszp2WS?80jGJr$O0xts=C=+ z0AP-0BLRIkyO?XL)=AmOVkxF$BYWd_D;wE~z}Nxr4lAb1^Z{+a&N!<1|<$U5Iw zRxBaS99|>5;$A8+ zg$Yj)35^VZxS__-2xA(;gQaD+1~znb0krJUJOG&Z8TzH+JZagnc>ptcZO)~I=bF+m z$x-a9QQ19?V Date: Mon, 20 Sep 2021 12:54:00 -0700 Subject: [PATCH 06/36] [CI] Increase heap for Jest Integration (#112594) Signed-off-by: Tyler Smalley --- .buildkite/scripts/steps/test/jest_integration.sh | 2 +- test/scripts/test/jest_integration.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite/scripts/steps/test/jest_integration.sh b/.buildkite/scripts/steps/test/jest_integration.sh index 9f0228fd910b..458651df2df2 100755 --- a/.buildkite/scripts/steps/test/jest_integration.sh +++ b/.buildkite/scripts/steps/test/jest_integration.sh @@ -10,4 +10,4 @@ is_test_execution_step echo '--- Jest Integration Tests' checks-reporter-with-killswitch "Jest Integration Tests" \ - node scripts/jest_integration --ci --verbose + node --max-old-space-size=5120 scripts/jest_integration --ci diff --git a/test/scripts/test/jest_integration.sh b/test/scripts/test/jest_integration.sh index aaffdb2fd9e9..89390657d1b4 100755 --- a/test/scripts/test/jest_integration.sh +++ b/test/scripts/test/jest_integration.sh @@ -3,4 +3,4 @@ source src/dev/ci_setup/setup_env.sh checks-reporter-with-killswitch "Jest Integration Tests" \ - node scripts/jest_integration --ci + node --max-old-space-size=5120 scripts/jest_integration --ci From 49977b2e4be9174c36d903da1d5ca3669d048181 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 20 Sep 2021 16:28:35 -0400 Subject: [PATCH 07/36] [Alerting] Track fields usage in find api (#112096) * Track fields usage * Add test * Add missing change --- .../alerting/server/routes/find_rules.test.ts | 39 ++++++++++++++++++- .../alerting/server/routes/find_rules.ts | 8 ++++ .../server/routes/legacy/find.test.ts | 26 +++++++++++++ .../alerting/server/routes/legacy/find.ts | 8 ++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index 23f636e96216..692fdca6c95a 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; import { findRulesRoute } from './find_rules'; import { httpServiceMock } from 'src/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; @@ -12,7 +12,6 @@ import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; import { trackLegacyTerminology } from './lib/track_legacy_terminology'; -import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); @@ -187,4 +186,40 @@ describe('findRulesRoute', () => { 'alertTypeId', ]); }); + + it('should track calls to deprecated functionality', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState, mockUsageCounter); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); + + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + fields: ['foo', 'bar'], + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `alertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 22d701b2040a..a4a066728555 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -136,6 +136,14 @@ export const findRulesRoute = ( search_fields: searchFieldsAsArray(req.query.search_fields), }); + if (req.query.fields) { + usageCounter?.incrementCounter({ + counterName: `alertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + } + const findResult = await rulesClient.find({ options }); return res.ok({ body: rewriteBodyRes(findResult), diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts index 1ddd1a662cbe..ef346fab1e62 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -194,4 +194,30 @@ describe('findAlertRoute', () => { 'alertTypeId', ]); }); + + it('should track calls to deprecated functionality', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + findAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + fields: ['foo', 'bar'], + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `legacyAlertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index 5a87536a1ac9..328fade49164 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -89,6 +89,14 @@ export const findAlertRoute = ( : [query.search_fields]; } + if (query.fields) { + usageCounter?.incrementCounter({ + counterName: `legacyAlertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + } + const findResult = await rulesClient.find({ options }); return res.ok({ body: findResult, From 817a0fba74daea722c47252eb8a5bd0147d2c283 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 20 Sep 2021 16:29:00 -0400 Subject: [PATCH 08/36] [CI] [Buildkite] increase xpack cigroup timeout and add snapshot verify timeouts (#112610) --- .buildkite/pipelines/es_snapshots/verify.yml | 5 +++++ .buildkite/pipelines/hourly.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index aa49ba5d2641..10c996c5acec 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -29,6 +29,7 @@ steps: agents: queue: ci-group-6 depends_on: build + timeout_in_minutes: 150 key: default-cigroup retry: automatic: @@ -40,6 +41,7 @@ steps: agents: queue: ci-group-6 depends_on: build + timeout_in_minutes: 120 key: default-cigroup-docker retry: automatic: @@ -52,6 +54,7 @@ steps: agents: queue: ci-group-4d depends_on: build + timeout_in_minutes: 120 key: oss-cigroup retry: automatic: @@ -62,6 +65,7 @@ steps: label: 'Jest Integration Tests' agents: queue: jest + timeout_in_minutes: 120 key: jest-integration retry: automatic: @@ -72,6 +76,7 @@ steps: label: 'API Integration Tests' agents: queue: jest + timeout_in_minutes: 120 key: api-integration - command: .buildkite/scripts/steps/es_snapshots/trigger_promote.sh diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 0dc8ad3e08d0..b78db4698c01 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -17,7 +17,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 120 + timeout_in_minutes: 150 key: default-cigroup retry: automatic: From e07b0e593ec062879d8da043bfbb5712d9ed4b16 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Mon, 20 Sep 2021 15:35:54 -0700 Subject: [PATCH 09/36] Revert "[Cases] Migrate user actions connector ID (#108272)" This reverts commit 10ac814d8f5ede2c10c83aadc99304a59ac9682f. --- x-pack/plugins/cases/common/api/cases/case.ts | 16 +- .../cases/common/api/cases/user_actions.ts | 3 +- .../cases/common/api/connectors/index.ts | 12 +- x-pack/plugins/cases/common/index.ts | 1 - x-pack/plugins/cases/common/ui/types.ts | 2 - .../cases/common/utils/user_actions.ts | 18 - .../cases/public/common/user_actions/index.ts | 8 - .../common/user_actions/parsers.test.ts | 86 - .../public/common/user_actions/parsers.ts | 77 - .../components/edit_connector/helpers.test.ts | 187 -- .../components/edit_connector/helpers.ts | 30 +- .../user_action_tree/helpers.test.tsx | 108 +- .../components/user_action_tree/helpers.tsx | 53 +- .../components/user_action_tree/index.tsx | 10 +- .../plugins/cases/public/containers/mock.ts | 123 +- .../use_get_case_user_actions.test.tsx | 237 +- .../containers/use_get_case_user_actions.tsx | 51 +- .../plugins/cases/public/containers/utils.ts | 8 + .../cases/server/client/attachments/add.ts | 6 +- .../cases/server/client/attachments/update.ts | 9 +- .../cases/server/client/cases/create.ts | 2 +- .../cases/server/client/cases/delete.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 18 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 6 +- .../cases/server/client/cases/utils.ts | 30 +- .../server/client/user_actions/get.test.ts | 106 - .../cases/server/client/user_actions/get.ts | 47 +- .../plugins/cases/server/common/constants.ts | 29 - .../migrations/cases.test.ts | 570 ++-- .../saved_object_types/migrations/cases.ts | 14 +- .../migrations/configuration.test.ts | 142 +- .../migrations/configuration.ts | 9 +- .../saved_object_types/migrations/index.ts | 57 +- .../migrations/user_actions.test.ts | 562 ---- .../migrations/user_actions.ts | 159 - .../migrations/utils.test.ts | 229 ++ .../saved_object_types/migrations/utils.ts | 73 + .../cases/server/services/cases/index.test.ts | 8 +- .../cases/server/services/test_utils.ts | 17 +- .../services/user_actions/helpers.test.ts | 332 -- .../server/services/user_actions/helpers.ts | 192 +- .../services/user_actions/index.test.ts | 557 ---- .../server/services/user_actions/index.ts | 101 +- .../services/user_actions/transform.test.ts | 1246 ------- .../server/services/user_actions/transform.ts | 320 -- .../server/services/user_actions/types.ts | 14 - .../tests/common/cases/delete_cases.ts | 4 +- .../tests/common/cases/patch_cases.ts | 4 - .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/post_comment.ts | 2 - .../user_actions/get_all_user_actions.ts | 19 +- .../tests/common/user_actions/migrations.ts | 149 +- .../tests/trial/cases/push_case.ts | 3 +- .../user_actions/get_all_user_actions.ts | 4 +- .../migrations/7.13_user_actions/data.json.gz | Bin 2078 -> 0 bytes .../7.13_user_actions/mappings.json | 2954 ----------------- 57 files changed, 1140 insertions(+), 7895 deletions(-) delete mode 100644 x-pack/plugins/cases/common/utils/user_actions.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/index.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.test.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.ts delete mode 100644 x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts delete mode 100644 x-pack/plugins/cases/server/client/user_actions/get.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/helpers.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/index.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/types.ts delete mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz delete mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 05a053307b29..37a491cdad4c 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -87,11 +87,8 @@ const CaseBasicRt = rt.type({ owner: rt.string, }); -/** - * This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field - * within the user action object in the API response. - */ -export const CaseUserActionExternalServiceRt = rt.type({ +export const CaseExternalServiceBasicRt = rt.type({ + connector_id: rt.union([rt.string, rt.null]), connector_name: rt.string, external_id: rt.string, external_title: rt.string, @@ -100,14 +97,7 @@ export const CaseUserActionExternalServiceRt = rt.type({ pushed_by: UserRT, }); -export const CaseExternalServiceBasicRt = rt.intersection([ - rt.type({ - connector_id: rt.union([rt.string, rt.null]), - }), - CaseUserActionExternalServiceRt, -]); - -export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); +const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); export const CaseAttributesRt = rt.intersection([ CaseBasicRt, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index e86ce5248a6f..03912c550d77 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -34,6 +34,7 @@ const UserActionRt = rt.union([ rt.literal('push-to-service'), ]); +// TO DO change state to status const CaseUserActionBasicRT = rt.type({ action_field: UserActionFieldRt, action: UserActionRt, @@ -50,8 +51,6 @@ const CaseUserActionResponseRT = rt.intersection([ action_id: rt.string, case_id: rt.string, comment_id: rt.union([rt.string, rt.null]), - new_val_connector_id: rt.union([rt.string, rt.null]), - old_val_connector_id: rt.union([rt.string, rt.null]), }), rt.partial({ sub_case_id: rt.string }), ]); diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 2b3483b4f618..77af90b5d08c 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -84,22 +84,14 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorSwimlaneTypeFieldsRt, ]); -/** - * This type represents the connector's format when it is encoded within a user action. - */ -export const CaseUserActionConnectorRt = rt.intersection([ - rt.type({ name: rt.string }), - ConnectorTypeFieldsRt, -]); - export const CaseConnectorRt = rt.intersection([ rt.type({ id: rt.string, + name: rt.string, }), - CaseUserActionConnectorRt, + ConnectorTypeFieldsRt, ]); -export type CaseUserActionConnector = rt.TypeOf; export type CaseConnector = rt.TypeOf; export type ConnectorTypeFields = rt.TypeOf; export type ConnectorJiraTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index d38b1a779981..5305318cc9aa 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -12,4 +12,3 @@ export * from './constants'; export * from './api'; export * from './ui/types'; export * from './utils/connectors_api'; -export * from './utils/user_actions'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index c89c3eb08263..bf4ec0da6ee5 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -66,9 +66,7 @@ export interface CaseUserActions { caseId: string; commentId: string | null; newValue: string | null; - newValConnectorId: string | null; oldValue: string | null; - oldValConnectorId: string | null; } export interface CaseExternalService { diff --git a/x-pack/plugins/cases/common/utils/user_actions.ts b/x-pack/plugins/cases/common/utils/user_actions.ts deleted file mode 100644 index 7de0d7066eae..000000000000 --- a/x-pack/plugins/cases/common/utils/user_actions.ts +++ /dev/null @@ -1,18 +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 function isCreateConnector(action?: string, actionFields?: string[]): boolean { - return action === 'create' && actionFields != null && actionFields.includes('connector'); -} - -export function isUpdateConnector(action?: string, actionFields?: string[]): boolean { - return action === 'update' && actionFields != null && actionFields.includes('connector'); -} - -export function isPush(action?: string, actionFields?: string[]): boolean { - return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed'); -} diff --git a/x-pack/plugins/cases/public/common/user_actions/index.ts b/x-pack/plugins/cases/public/common/user_actions/index.ts deleted file mode 100644 index 507455f7102a..000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/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 './parsers'; diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts deleted file mode 100644 index c6d13cc41686..000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts +++ /dev/null @@ -1,86 +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 { ConnectorTypes, noneConnectorId } from '../../../common'; -import { parseStringAsConnector, parseStringAsExternalService } from './parsers'; - -describe('user actions utility functions', () => { - describe('parseStringAsConnector', () => { - it('return null if the data is null', () => { - expect(parseStringAsConnector('', null)).toBeNull(); - }); - - it('return null if the data is not a json object', () => { - expect(parseStringAsConnector('', 'blah')).toBeNull(); - }); - - it('return null if the data is not a valid connector', () => { - expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull(); - }); - - it('return null if id is null but the data is a connector other than none', () => { - expect( - parseStringAsConnector( - null, - JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null }) - ) - ).toBeNull(); - }); - - it('return the id as the none connector if the data is the none connector', () => { - expect( - parseStringAsConnector( - null, - JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }) - ) - ).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null }); - }); - - it('returns a decoded connector with the specified id', () => { - expect( - parseStringAsConnector( - 'a', - JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null }) - ) - ).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null }); - }); - }); - - describe('parseStringAsExternalService', () => { - it('returns null when the data is null', () => { - expect(parseStringAsExternalService('', null)).toBeNull(); - }); - - it('returns null when the data is not valid json', () => { - expect(parseStringAsExternalService('', 'blah')).toBeNull(); - }); - - it('returns null when the data is not a valid external service object', () => { - expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull(); - }); - - it('returns the decoded external service with the connector_id field added', () => { - const externalServiceInfo = { - connector_name: 'name', - external_id: '1', - external_title: 'title', - external_url: 'abc', - pushed_at: '1', - pushed_by: { - username: 'a', - email: 'a@a.com', - full_name: 'a', - }, - }; - - expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({ - ...externalServiceInfo, - connector_id: '500', - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.ts deleted file mode 100644 index dfea22443aa5..000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/parsers.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - CaseUserActionConnectorRt, - CaseConnector, - ConnectorTypes, - noneConnectorId, - CaseFullExternalService, - CaseUserActionExternalServiceRt, -} from '../../../common'; - -export const parseStringAsConnector = ( - id: string | null, - encodedData: string | null -): CaseConnector | null => { - if (encodedData == null) { - return null; - } - - const decodedConnector = parseString(encodedData); - - if (!CaseUserActionConnectorRt.is(decodedConnector)) { - return null; - } - - if (id == null && decodedConnector.type === ConnectorTypes.none) { - return { - ...decodedConnector, - id: noneConnectorId, - }; - } else if (id == null) { - return null; - } else { - // id does not equal null or undefined and the connector type does not equal none - // so return the connector with its id - return { - ...decodedConnector, - id, - }; - } -}; - -const parseString = (params: string | null): unknown | null => { - if (params == null) { - return null; - } - - try { - return JSON.parse(params); - } catch { - return null; - } -}; - -export const parseStringAsExternalService = ( - id: string | null, - encodedData: string | null -): CaseFullExternalService => { - if (encodedData == null) { - return null; - } - - const decodedExternalService = parseString(encodedData); - if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) { - return null; - } - - return { - ...decodedExternalService, - connector_id: id, - }; -}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts deleted file mode 100644 index e20d6b37258b..000000000000 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts +++ /dev/null @@ -1,187 +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 { CaseUserActionConnector, ConnectorTypes } from '../../../common'; -import { CaseUserActions } from '../../containers/types'; -import { getConnectorFieldsFromUserActions } from './helpers'; - -describe('helpers', () => { - describe('getConnectorFieldsFromUserActions', () => { - it('returns null when it cannot find the connector id', () => { - expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); - }); - - it('returns null when the value fields are not valid encoded fields', () => { - expect( - getConnectorFieldsFromUserActions('a', [createUserAction({ newValue: 'a', oldValue: 'a' })]) - ).toBeNull(); - }); - - it('returns null when it cannot find the connector id in a non empty array', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: JSON.stringify({ a: '1' }), - oldValue: JSON.stringify({ a: '1' }), - }), - ]) - ).toBeNull(); - }); - - it('returns the fields when it finds the connector id in the new value', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: JSON.stringify({ a: '1' }), - newValConnectorId: 'a', - }), - ]) - ).toEqual(defaultJiraFields); - }); - - it('returns the fields when it finds the connector id in the new value and the old value is null', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - newValConnectorId: 'a', - }), - ]) - ).toEqual(defaultJiraFields); - }); - - it('returns the fields when it finds the connector id in the old value', () => { - const expectedFields = { ...defaultJiraFields, issueType: '5' }; - - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector({ - fields: expectedFields, - }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - }), - ]) - ).toEqual(expectedFields); - }); - - it('returns the fields when it finds the connector id in the second user action', () => { - const expectedFields = { ...defaultJiraFields, issueType: '5' }; - - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector(), - newValConnectorId: 'b', - oldValConnectorId: 'a', - }), - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector({ fields: expectedFields }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - }), - ]) - ).toEqual(expectedFields); - }); - - it('ignores a parse failure and finds the right user action', () => { - expect( - getConnectorFieldsFromUserActions('none', [ - createUserAction({ - newValue: 'b', - newValConnectorId: null, - }), - createUserAction({ - newValue: createEncodedJiraConnector({ - type: ConnectorTypes.none, - name: '', - fields: null, - }), - newValConnectorId: null, - }), - ]) - ).toBeNull(); - }); - - it('returns null when the id matches but the encoded value is null', () => { - expect( - getConnectorFieldsFromUserActions('b', [ - createUserAction({ - newValue: null, - newValConnectorId: 'b', - }), - ]) - ).toBeNull(); - }); - - it('returns null when the action fields is not of length 1', () => { - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: JSON.stringify({ a: '1', fields: { hello: '1' } }), - oldValue: JSON.stringify({ a: '1', fields: { hi: '2' } }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - actionField: ['connector', 'connector'], - }), - ]) - ).toBeNull(); - }); - - it('matches the none connector the searched for id is none', () => { - expect( - getConnectorFieldsFromUserActions('none', [ - createUserAction({ - newValue: createEncodedJiraConnector({ - type: ConnectorTypes.none, - name: '', - fields: null, - }), - newValConnectorId: null, - }), - ]) - ).toBeNull(); - }); - }); -}); - -function createUserAction(fields: Partial): CaseUserActions { - return { - action: 'update', - actionAt: '', - actionBy: {}, - actionField: ['connector'], - actionId: '', - caseId: '', - commentId: '', - newValConnectorId: null, - oldValConnectorId: null, - newValue: null, - oldValue: null, - ...fields, - }; -} - -function createEncodedJiraConnector(fields?: Partial): string { - return JSON.stringify({ - type: ConnectorTypes.jira, - name: 'name', - fields: defaultJiraFields, - ...fields, - }); -} - -const defaultJiraFields = { - issueType: '1', - parent: null, - priority: null, -}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts index b97035c458ac..36eb3f58c8aa 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts @@ -5,33 +5,23 @@ * 2.0. */ -import { ConnectorTypeFields } from '../../../common'; import { CaseUserActions } from '../../containers/types'; -import { parseStringAsConnector } from '../../common/user_actions'; -export const getConnectorFieldsFromUserActions = ( - id: string, - userActions: CaseUserActions[] -): ConnectorTypeFields['fields'] => { +export const getConnectorFieldsFromUserActions = (id: string, userActions: CaseUserActions[]) => { try { for (const action of [...userActions].reverse()) { if (action.actionField.length === 1 && action.actionField[0] === 'connector') { - const parsedNewConnector = parseStringAsConnector( - action.newValConnectorId, - action.newValue - ); + if (action.oldValue && action.newValue) { + const oldValue = JSON.parse(action.oldValue); + const newValue = JSON.parse(action.newValue); - if (parsedNewConnector && id === parsedNewConnector.id) { - return parsedNewConnector.fields; - } - - const parsedOldConnector = parseStringAsConnector( - action.oldValConnectorId, - action.oldValue - ); + if (newValue.id === id) { + return newValue.fields; + } - if (parsedOldConnector && id === parsedOldConnector.id) { - return parsedOldConnector.fields; + if (oldValue.id === id) { + return oldValue.fields; + } } } } diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx index 841f0d36bbf1..b49a010cff38 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses, ConnectorTypes } from '../../../common'; +import { CaseStatuses } from '../../../common'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, @@ -129,7 +129,7 @@ describe('User action tree helpers', () => { `${i18n.PUSHED_NEW_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue!).external_url + JSON.parse(action.newValue).external_url ); }); @@ -142,74 +142,50 @@ describe('User action tree helpers', () => { `${i18n.UPDATE_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue!).external_url + JSON.parse(action.newValue).external_url ); }); - describe('getConnectorLabelTitle', () => { - it('returns an empty string when the encoded old value is null', () => { - const result = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { oldValue: null }), - connectors, - }); - - expect(result).toEqual(''); - }); - - it('returns an empty string when the encoded new value is null', () => { - const result = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { newValue: null }), - connectors, - }); - - expect(result).toEqual(''); - }); - - it('returns the change connector label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ - type: ConnectorTypes.serviceNowITSM, - name: 'a', - fields: null, - }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.resilient, name: 'a', fields: null }), - newValConnectorId: 'resilient-2', - }), - connectors, - }); - - expect(result).toEqual('selected My Connector 2 as incident management system'); - }); - - it('returns the removed connector label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }), - newValConnectorId: 'none', - }), - connectors, - }); - - expect(result).toEqual('removed external incident management system'); - }); - - it('returns the connector fields changed label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - newValConnectorId: 'servicenow-1', - }), - connectors, - }); - - expect(result).toEqual('changed connector field'); + it('label title generated for update connector - change connector', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'resilient-2' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, + }); + + expect(result).toEqual('selected My Connector 2 as incident management system'); + }); + + it('label title generated for update connector - change connector to none', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'none' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, }); + + expect(result).toEqual('removed external incident management system'); + }); + + it('label title generated for update connector - field change', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'servicenow-1' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, + }); + + expect(result).toEqual('changed connector field'); }); describe('toStringArray', () => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx index 2eb44f91190c..744b14926b35 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx @@ -23,11 +23,10 @@ import { CommentType, Comment, CommentRequestActionsType, - noneConnectorId, } from '../../../common'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseStringAsConnector, parseStringAsExternalService } from '../../common/user_actions'; +import { parseString } from '../../containers/utils'; import { Tags } from '../tag_list/tags'; import { UserActionUsernameWithAvatar } from './user_action_username_with_avatar'; import { UserActionTimestamp } from './user_action_timestamp'; @@ -98,27 +97,23 @@ export const getConnectorLabelTitle = ({ action: CaseUserActions; connectors: ActionConnector[]; }) => { - const oldConnector = parseStringAsConnector(action.oldValConnectorId, action.oldValue); - const newConnector = parseStringAsConnector(action.newValConnectorId, action.newValue); + const oldValue = parseString(`${action.oldValue}`); + const newValue = parseString(`${action.newValue}`); - if (!oldConnector || !newConnector) { + if (oldValue === null || newValue === null) { return ''; } - // if the ids are the same, assume we just changed the fields - if (oldConnector.id === newConnector.id) { + // Connector changed + if (oldValue.id !== newValue.id) { + const newConnector = connectors.find((c) => c.id === newValue.id); + return newValue.id != null && newValue.id !== 'none' && newConnector != null + ? i18n.SELECTED_THIRD_PARTY(newConnector.name) + : i18n.REMOVED_THIRD_PARTY; + } else { + // Field changed return i18n.CHANGED_CONNECTOR_FIELD; } - - // ids are not the same so check and see if the id is a valid connector and then return its name - // if the connector id is the none connector value then it must have been removed - const newConnectorActionInfo = connectors.find((c) => c.id === newConnector.id); - if (newConnector.id !== noneConnectorId && newConnectorActionInfo != null) { - return i18n.SELECTED_THIRD_PARTY(newConnectorActionInfo.name); - } - - // it wasn't a valid connector or it was the none connector, so it must have been removed - return i18n.REMOVED_THIRD_PARTY; }; const getTagsLabelTitle = (action: CaseUserActions) => { @@ -138,8 +133,7 @@ const getTagsLabelTitle = (action: CaseUserActions) => { }; export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: boolean) => { - const externalService = parseStringAsExternalService(action.newValConnectorId, action.newValue); - + const pushedVal = JSON.parse(action.newValue ?? '') as CaseFullExternalService; return ( {`${firstPush ? i18n.PUSHED_NEW_INCIDENT : i18n.UPDATE_INCIDENT} ${ - externalService?.connector_name + pushedVal?.connector_name }`} - - {externalService?.external_title} + + {pushedVal?.external_title} @@ -163,19 +157,20 @@ export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: b export const getPushInfo = ( caseServices: CaseServices, - externalService: CaseFullExternalService | undefined, + // a JSON parse failure will result in null for parsedValue + parsedValue: { connector_id: string | null; connector_name: string } | null, index: number ) => - externalService != null && externalService.connector_id != null + parsedValue != null && parsedValue.connector_id != null ? { - firstPush: caseServices[externalService.connector_id]?.firstPushIndex === index, - parsedConnectorId: externalService.connector_id, - parsedConnectorName: externalService.connector_name, + firstPush: caseServices[parsedValue.connector_id]?.firstPushIndex === index, + parsedConnectorId: parsedValue.connector_id, + parsedConnectorName: parsedValue.connector_name, } : { firstPush: false, - parsedConnectorId: noneConnectorId, - parsedConnectorName: noneConnectorId, + parsedConnectorId: 'none', + parsedConnectorName: 'none', }; const getUpdateActionIcon = (actionField: string): string => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx index 7ea415324194..784817229caf 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx @@ -35,7 +35,7 @@ import { Ecs, } from '../../../common'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseStringAsExternalService } from '../../common/user_actions'; +import { parseString } from '../../containers/utils'; import { OnUpdateFields } from '../case_view'; import { getConnectorLabelTitle, @@ -512,14 +512,10 @@ export const UserActionTree = React.memo( // Pushed information if (action.actionField.length === 1 && action.actionField[0] === 'pushed') { - const parsedExternalService = parseStringAsExternalService( - action.newValConnectorId, - action.newValue - ); - + const parsedValue = parseString(`${action.newValue}`); const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( caseServices, - parsedExternalService, + parsedValue, index ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index fcd564969d48..c955bb34240e 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -9,7 +9,6 @@ import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } import { AssociationType, - CaseUserActionConnector, CaseResponse, CasesFindResponse, CasesResponse, @@ -20,9 +19,6 @@ import { CommentResponse, CommentType, ConnectorTypes, - isCreateConnector, - isPush, - isUpdateConnector, SECURITY_SOLUTION_OWNER, UserAction, UserActionField, @@ -244,9 +240,7 @@ export const pushedCase: Case = { const basicAction = { actionAt: basicCreatedAt, actionBy: elasticUser, - oldValConnectorId: null, oldValue: null, - newValConnectorId: null, newValue: 'what a cool value', caseId: basicCaseId, commentId: null, @@ -314,7 +308,12 @@ export const basicCaseSnake: CaseResponse = { closed_at: null, closed_by: null, comments: [basicCommentSnake], - connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, + connector: { + id: 'none', + name: 'My Connector', + type: ConnectorTypes.none, + fields: null, + }, created_at: basicCreatedAt, created_by: elasticUserSnake, external_service: null, @@ -329,8 +328,8 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; -export const pushConnectorId = '123'; export const pushSnake = { + connector_id: '123', connector_name: 'connector name', external_id: 'external_id', external_title: 'external title', @@ -351,7 +350,7 @@ export const pushedCaseSnake = { type: ConnectorTypes.jira, fields: null, }, - external_service: { ...basicPushSnake, connector_id: pushConnectorId }, + external_service: basicPushSnake, }; export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; @@ -386,20 +385,17 @@ const basicActionSnake = { comment_id: null, owner: SECURITY_SOLUTION_OWNER, }; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => { - const isPushToService = a === 'push-to-service' && af[0] === 'pushed'; - - return { - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: isPushToService ? JSON.stringify(basicPushSnake) : basicAction.newValue, - new_val_connector_id: isPushToService ? pushConnectorId : null, - old_val_connector_id: null, - }; -}; +export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ + ...basicActionSnake, + action_id: `${af[0]}-${a}`, + action_field: af, + action: a, + comment_id: af[0] === 'comment' ? basicCommentId : null, + new_value: + a === 'push-to-service' && af[0] === 'pushed' + ? JSON.stringify(basicPushSnake) + : basicAction.newValue, +}); export const caseUserActionsSnake: CaseUserActionsResponse = [ getUserActionSnake(['description'], 'create'), @@ -409,76 +405,17 @@ export const caseUserActionsSnake: CaseUserActionsResponse = [ // user actions -export const getUserAction = ( - af: UserActionField, - a: UserAction, - overrides?: Partial -): CaseUserActions => { - return { - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - ...getValues(a, af, overrides), - }; -}; - -const getValues = ( - userAction: UserAction, - actionFields: UserActionField, - overrides?: Partial -): Partial => { - if (isCreateConnector(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined ? JSON.stringify(basicCaseSnake) : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: null, - oldValConnectorId: null, - }; - } else if (isUpdateConnector(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined - ? JSON.stringify({ name: 'My Connector', type: ConnectorTypes.none, fields: null }) - : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: - overrides?.oldValue === undefined - ? JSON.stringify({ name: 'My Connector2', type: ConnectorTypes.none, fields: null }) - : overrides.oldValue, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } else if (isPush(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined ? JSON.stringify(basicPushSnake) : overrides?.newValue, - newValConnectorId: - overrides?.newValConnectorId === undefined ? pushConnectorId : overrides.newValConnectorId, - oldValue: overrides?.oldValue ?? null, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } else { - return { - newValue: overrides?.newValue === undefined ? basicAction.newValue : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: overrides?.oldValue ?? null, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } -}; - -export const getJiraConnectorWithoutId = (overrides?: Partial) => { - return JSON.stringify({ - name: 'jira1', - type: ConnectorTypes.jira, - ...jiraFields, - ...overrides, - }); -}; - -export const jiraFields = { fields: { issueType: '10006', priority: null, parent: null } }; +export const getUserAction = (af: UserActionField, a: UserAction) => ({ + ...basicAction, + actionId: `${af[0]}-${a}`, + actionField: af, + action: a, + commentId: af[0] === 'comment' ? basicCommentId : null, + newValue: + a === 'push-to-service' && af[0] === 'pushed' + ? JSON.stringify(basicPushSnake) + : basicAction.newValue, +}); export const getAlertUserAction = () => ({ ...basicAction, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index e7e46fa46c7c..62b4cf92434c 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -18,9 +18,7 @@ import { basicPushSnake, caseUserActions, elasticUser, - getJiraConnectorWithoutId, getUserAction, - jiraFields, } from './mock'; import * as api from './api'; @@ -301,14 +299,15 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, @@ -347,14 +346,15 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, @@ -392,7 +392,11 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -414,7 +418,11 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123To456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -436,8 +444,16 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123To456UserAction(), - createChangeConnector456To123UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -458,10 +474,22 @@ describe('useGetCaseUserActions', () => { it('Change fields and connector after push - hasDataToPush: true', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createChangeConnector456To123PriorityLowUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -482,10 +510,22 @@ describe('useGetCaseUserActions', () => { it('Change only connector after push - hasDataToPush: false', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createChangeConnector456To123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -507,24 +547,45 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, pushAction123, - createChangeConnector123HighPriorityTo456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, pushAction456, - createChangeConnector456To123PriorityLowUserAction(), - createChangeConnector123LowPriorityTo456UserAction(), - createChangeConnector456To123PriorityLowUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -556,22 +617,34 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { - newValConnectorId: '456', + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - }); - + }; const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, pushAction123, - createChangeConnector123HighPriorityTo456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, pushAction456, - createChangeConnector456To123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -602,10 +675,22 @@ describe('useGetCaseUserActions', () => { it('Changing other connectors fields does not count as an update', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createUpdateConnectorFields456HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '3' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -624,83 +709,3 @@ describe('useGetCaseUserActions', () => { }); }); }); - -const jira123HighPriorityFields = { - fields: { ...jiraFields.fields, priority: 'High' }, -}; - -const jira123LowPriorityFields = { - fields: { ...jiraFields.fields, priority: 'Low' }, -}; - -const jira456Fields = { - fields: { issueType: '10', parent: null, priority: null }, -}; - -const jira456HighPriorityFields = { - fields: { ...jira456Fields.fields, priority: 'High' }, -}; - -const createUpdateConnectorFields123HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(), - newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - oldValConnectorId: '123', - newValConnectorId: '123', - }); - -const createUpdateConnectorFields456HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - newValue: getJiraConnectorWithoutId(jira456HighPriorityFields), - oldValConnectorId: '456', - newValConnectorId: '456', - }); - -const createChangeConnector123HighPriorityTo456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector123To456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector123LowPriorityTo456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira123LowPriorityFields), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector456To123UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(), - newValConnectorId: '123', - }); - -const createChangeConnector456To123HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - newValConnectorId: '123', - }); - -const createChangeConnector456To123PriorityLowUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(jira123LowPriorityFields), - newValConnectorId: '123', - }); 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 36d600c3f1c9..e481519ba19a 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 @@ -18,8 +18,7 @@ import { } from '../../common'; import { getCaseUserActions, getSubCaseUserActions } from './api'; import * as i18n from './translations'; -import { convertToCamelCase } from './utils'; -import { parseStringAsConnector, parseStringAsExternalService } from '../common/user_actions'; +import { convertToCamelCase, parseString } from './utils'; import { useToasts } from '../common/lib/kibana'; export interface CaseService extends CaseExternalService { @@ -59,24 +58,8 @@ export interface UseGetCaseUserActions extends CaseUserActionsState { ) => Promise; } -const unknownExternalServiceConnectorId = 'unknown'; - -const getExternalService = ( - connectorId: string | null, - encodedValue: string | null -): CaseExternalService | null => { - const decodedValue = parseStringAsExternalService(connectorId, encodedValue); - - if (decodedValue == null) { - return null; - } - return { - ...convertToCamelCase(decodedValue), - // if in the rare case that the connector id is null we'll set it to unknown if we need to reference it in the UI - // anywhere. The id would only ever be null if a migration failed or some logic error within the backend occurred - connectorId: connectorId ?? unknownExternalServiceConnectorId, - }; -}; +const getExternalService = (value: string): CaseExternalService | null => + convertToCamelCase(parseString(`${value}`)); const groupConnectorFields = ( userActions: CaseUserActions[] @@ -86,26 +69,22 @@ const groupConnectorFields = ( return acc; } - const oldConnector = parseStringAsConnector(mua.oldValConnectorId, mua.oldValue); - const newConnector = parseStringAsConnector(mua.newValConnectorId, mua.newValue); + const oldValue = parseString(`${mua.oldValue}`); + const newValue = parseString(`${mua.newValue}`); - if (!oldConnector || !newConnector) { + if (oldValue == null || newValue == null) { return acc; } return { ...acc, - [oldConnector.id]: [ - ...(acc[oldConnector.id] || []), - ...(oldConnector.id === newConnector.id - ? [oldConnector.fields, newConnector.fields] - : [oldConnector.fields]), + [oldValue.id]: [ + ...(acc[oldValue.id] || []), + ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [oldValue.fields]), ], - [newConnector.id]: [ - ...(acc[newConnector.id] || []), - ...(oldConnector.id === newConnector.id - ? [oldConnector.fields, newConnector.fields] - : [newConnector.fields]), + [newValue.id]: [ + ...(acc[newValue.id] || []), + ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [newValue.fields]), ], }; }, {} as Record>); @@ -158,7 +137,9 @@ export const getPushedInfo = ( const hasDataToPushForConnector = (connectorId: string): boolean => { const caseUserActionsReversed = [...caseUserActions].reverse(); const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => mua.action === 'push-to-service' && mua.newValConnectorId === connectorId + (mua) => + mua.action === 'push-to-service' && + getExternalService(`${mua.newValue}`)?.connectorId === connectorId ); if (lastPushOfConnectorReversedIndex === -1) { @@ -209,7 +190,7 @@ export const getPushedInfo = ( return acc; } - const externalService = getExternalService(cua.newValConnectorId, cua.newValue); + const externalService = getExternalService(`${cua.newValue}`); if (externalService === null) { return acc; } diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index b0cc0c72fee7..de67b1cfbd6f 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -36,6 +36,14 @@ import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; +export const parseString = (params: string) => { + try { + return JSON.parse(params); + } catch { + return null; + } +}; + export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { if (isArray(value)) { diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index b84a6bd84c43..507405d58cef 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -106,7 +106,7 @@ async function getSubCase({ caseId, subCaseId: newSubCase.id, fields: ['status', 'sub_case'], - newValue: { status: newSubCase.attributes.status }, + newValue: JSON.stringify({ status: newSubCase.attributes.status }), owner: newSubCase.attributes.owner, }), ], @@ -220,7 +220,7 @@ const addGeneratedAlerts = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: query, + newValue: JSON.stringify(query), owner: newComment.attributes.owner, }), ], @@ -408,7 +408,7 @@ export const addComment = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: query, + newValue: JSON.stringify(query), owner: newComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index b5e9e6c37235..9816efd9a845 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -17,7 +17,6 @@ import { SUB_CASE_SAVED_OBJECT, CaseResponse, CommentPatchRequest, - CommentRequest, } from '../../../common'; import { AttachmentService, CasesService } from '../../services'; import { CasesClientArgs } from '..'; @@ -194,12 +193,12 @@ export async function update( subCaseId: subCaseID, commentId: updatedComment.id, fields: ['comment'], - // casting because typescript is complaining that it's not a Record even though it is - newValue: queryRestAttributes as CommentRequest, - oldValue: + newValue: JSON.stringify(queryRestAttributes), + oldValue: JSON.stringify( // We are interested only in ContextBasicRt attributes // myComment.attribute contains also CommentAttributesBasicRt attributes - pick(Object.keys(queryRestAttributes), myComment.attributes), + pick(Object.keys(queryRestAttributes), myComment.attributes) + ), owner: myComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 488bc523f779..887990fef893 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -106,7 +106,7 @@ export const create = async ( actionBy: { username, full_name, email }, caseId: newCase.id, fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', OWNER_FIELD], - newValue: query, + newValue: JSON.stringify(query), owner: newCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 4333535f17a2..80a687a0e72f 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -168,7 +168,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P 'settings', OWNER_FIELD, 'comment', - ...(ENABLE_CASE_CONNECTOR ? ['sub_case' as const] : []), + ...(ENABLE_CASE_CONNECTOR ? ['sub_case'] : []), ], owner: caseInfo.attributes.owner, }) diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 22520cea1101..313d6cd12a6d 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -231,10 +231,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', - new_val_connector_id: '456', + '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"id":"456","name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', old_value: null, - old_val_connector_id: null, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -250,9 +248,7 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '456', - old_val_connector_id: null, + '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -269,8 +265,6 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: '{"type":"alert","alertId":"alert-id-1","index":".siem-signals-default-000008"}', - new_val_connector_id: null, - old_val_connector_id: null, old_value: null, action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -288,8 +282,6 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"type":"alert","alertId":"alert-id-2","index":".siem-signals-default-000008"}', old_value: null, - new_val_connector_id: null, - old_val_connector_id: null, action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', @@ -305,10 +297,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '456', + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, - old_val_connector_id: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -325,8 +315,6 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"comment":"a comment!","type":"user"}', old_value: null, - new_val_connector_id: null, - old_val_connector_id: null, action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 1b090a653546..3048cf01bb3b 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,7 @@ export const push = async ( actionBy: { username, full_name, email }, caseId, fields: ['pushed'], - newValue: externalService, + newValue: JSON.stringify(externalService), owner: myCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 315e9966d347..d7c45d3e1e9a 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -799,10 +799,8 @@ describe('utils', () => { username: 'elastic', }, new_value: - // The connector id is 123 - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '123', - old_val_connector_id: null, + // The connector id is 123 + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"123","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index f5cf2fe4b3f5..359ad4b41ead 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,8 +20,6 @@ import { CommentRequestUserType, CommentRequestAlertType, CommentRequestActionsType, - CaseUserActionResponse, - isPush, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -57,36 +55,22 @@ export const getLatestPushInfo = ( userActions: CaseUserActionsResponse ): { index: number; pushedInfo: CaseFullExternalService } | null => { for (const [index, action] of [...userActions].reverse().entries()) { - if ( - isPush(action.action, action.action_field) && - isValidNewValue(action) && - connectorId === action.new_val_connector_id - ) { + if (action.action === 'push-to-service' && action.new_value) try { const pushedInfo = JSON.parse(action.new_value); - // We returned the index of the element in the userActions array. - // As we traverse the userActions in reverse we need to calculate the index of a normal traversal - return { - index: userActions.length - index - 1, - pushedInfo: { ...pushedInfo, connector_id: connectorId }, - }; + if (pushedInfo.connector_id === connectorId) { + // We returned the index of the element in the userActions array. + // As we traverse the userActions in reverse we need to calculate the index of a normal traversal + return { index: userActions.length - index - 1, pushedInfo }; + } } catch (e) { - // ignore parse failures and check the next user action + // Silence JSON parse errors } - } } return null; }; -type NonNullNewValueAction = Omit & { - new_value: string; - new_val_connector_id: string; -}; - -const isValidNewValue = (userAction: CaseUserActionResponse): userAction is NonNullNewValueAction => - userAction.new_val_connector_id != null && userAction.new_value != null; - const getCommentContent = (comment: CommentResponse): string => { if (comment.type === CommentType.user) { return comment.comment; diff --git a/x-pack/plugins/cases/server/client/user_actions/get.test.ts b/x-pack/plugins/cases/server/client/user_actions/get.test.ts deleted file mode 100644 index 302e069cde4d..000000000000 --- a/x-pack/plugins/cases/server/client/user_actions/get.test.ts +++ /dev/null @@ -1,106 +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 { CaseUserActionResponse, SUB_CASE_SAVED_OBJECT } from '../../../common'; -import { SUB_CASE_REF_NAME } from '../../common'; -import { extractAttributesWithoutSubCases } from './get'; - -describe('get', () => { - describe('extractAttributesWithoutSubCases', () => { - it('returns an empty array when given an empty array', () => { - expect( - extractAttributesWithoutSubCases({ ...getFindResponseFields(), saved_objects: [] }) - ).toEqual([]); - }); - - it('filters out saved objects with a sub case reference', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], - id: 'b', - score: 0, - attributes: {} as CaseUserActionResponse, - }, - ], - }) - ).toEqual([]); - }); - - it('filters out saved objects with a sub case reference with other references', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: {} as CaseUserActionResponse, - }, - ], - }) - ).toEqual([]); - }); - - it('keeps saved objects that do not have a sub case reference', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: { field: '1' } as unknown as CaseUserActionResponse, - }, - ], - }) - ).toEqual([{ field: '1' }]); - }); - - it('filters multiple saved objects correctly', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: { field: '2' } as unknown as CaseUserActionResponse, - }, - { - type: 'a', - references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], - id: 'b', - score: 0, - attributes: { field: '1' } as unknown as CaseUserActionResponse, - }, - ], - }) - ).toEqual([{ field: '2' }]); - }); - }); -}); - -const getFindResponseFields = () => ({ page: 1, per_page: 1, total: 0 }); diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 660cf1b6a336..2a6608014c80 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { SavedObjectReference, SavedObjectsFindResponse } from 'kibana/server'; import { + CASE_COMMENT_SAVED_OBJECT, + CASE_SAVED_OBJECT, CaseUserActionsResponse, CaseUserActionsResponseRt, SUB_CASE_SAVED_OBJECT, - CaseUserActionResponse, } from '../../../common'; -import { createCaseError, checkEnabledCaseConnectorOrThrow, SUB_CASE_REF_NAME } from '../../common'; +import { createCaseError, checkEnabledCaseConnectorOrThrow } from '../../common'; import { CasesClientArgs } from '..'; import { Operations } from '../../authorization'; import { UserActionGet } from './client'; @@ -40,12 +40,23 @@ export const get = async ( operation: Operations.getUserActions, }); - const resultsToEncode = - subCaseId == null - ? extractAttributesWithoutSubCases(userActions) - : extractAttributes(userActions); - - return CaseUserActionsResponseRt.encode(resultsToEncode); + return CaseUserActionsResponseRt.encode( + userActions.saved_objects.reduce((acc, ua) => { + if (subCaseId == null && ua.references.some((uar) => uar.type === SUB_CASE_SAVED_OBJECT)) { + return acc; + } + return [ + ...acc, + { + ...ua.attributes, + action_id: ua.id, + case_id: ua.references.find((r) => r.type === CASE_SAVED_OBJECT)?.id ?? '', + comment_id: ua.references.find((r) => r.type === CASE_COMMENT_SAVED_OBJECT)?.id ?? null, + sub_case_id: ua.references.find((r) => r.type === SUB_CASE_SAVED_OBJECT)?.id ?? '', + }, + ]; + }, []) + ); } catch (error) { throw createCaseError({ message: `Failed to retrieve user actions case id: ${caseId} sub case id: ${subCaseId}: ${error}`, @@ -54,21 +65,3 @@ export const get = async ( }); } }; - -export function extractAttributesWithoutSubCases( - userActions: SavedObjectsFindResponse -): CaseUserActionsResponse { - // exclude user actions relating to sub cases from the results - const hasSubCaseReference = (references: SavedObjectReference[]) => - references.find((ref) => ref.type === SUB_CASE_SAVED_OBJECT && ref.name === SUB_CASE_REF_NAME); - - return userActions.saved_objects - .filter((so) => !hasSubCaseReference(so.references)) - .map((so) => so.attributes); -} - -function extractAttributes( - userActions: SavedObjectsFindResponse -): CaseUserActionsResponse { - return userActions.saved_objects.map((so) => so.attributes); -} diff --git a/x-pack/plugins/cases/server/common/constants.ts b/x-pack/plugins/cases/server/common/constants.ts index eba0a64a5c0b..1f6af310d6ec 100644 --- a/x-pack/plugins/cases/server/common/constants.ts +++ b/x-pack/plugins/cases/server/common/constants.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common'; - /** * The name of the saved object reference indicating the action connector ID. This is stored in the Saved Object reference * field's name property. @@ -17,30 +15,3 @@ export const CONNECTOR_ID_REFERENCE_NAME = 'connectorId'; * The name of the saved object reference indicating the action connector ID that was used to push a case. */ export const PUSH_CONNECTOR_ID_REFERENCE_NAME = 'pushConnectorId'; - -/** - * The name of the saved object reference indicating the action connector ID that was used for - * adding a connector, or updating the existing connector for a user action's old_value field. - */ -export const USER_ACTION_OLD_ID_REF_NAME = 'oldConnectorId'; - -/** - * The name of the saved object reference indicating the action connector ID that was used for pushing a case, - * for a user action's old_value field. - */ -export const USER_ACTION_OLD_PUSH_ID_REF_NAME = 'oldPushConnectorId'; - -/** - * The name of the saved object reference indicating the caseId reference - */ -export const CASE_REF_NAME = `associated-${CASE_SAVED_OBJECT}`; - -/** - * The name of the saved object reference indicating the commentId reference - */ -export const COMMENT_REF_NAME = `associated-${CASE_COMMENT_SAVED_OBJECT}`; - -/** - * The name of the saved object reference indicating the subCaseId reference - */ -export const SUB_CASE_REF_NAME = `associated-${SUB_CASE_SAVED_OBJECT}`; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index 9020f65ae352..bca12a86a544 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -30,324 +30,322 @@ const create_7_14_0_case = ({ }, }); -describe('case migrations', () => { - describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector.id is none', () => { - const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('does not create a reference when the connector is undefined', () => { - const caseSavedObject = create_7_14_0_case(); +describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector.id is none', () => { + const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the connector is undefined', () => { + const caseSavedObject = create_7_14_0_case(); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); + it('sets the connector to the default none connector if the connector.id is undefined', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + fields: null, + name: ConnectorTypes.jira, + type: ConnectorTypes.jira, + } as ESCaseConnectorWithId, }); - it('sets the connector to the default none connector if the connector.id is undefined', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - fields: null, - name: ConnectorTypes.jira, - type: ConnectorTypes.jira, - } as ESCaseConnectorWithId, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the external_service is null', () => { - const caseSavedObject = create_7_14_0_case({ externalService: null }); + it('does not create a reference when the external_service is null', () => { + const caseSavedObject = create_7_14_0_case({ externalService: null }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); + + it('does not create a reference when the external_service is undefined and sets external_service to null', () => { + const caseSavedObject = create_7_14_0_case(); - it('does not create a reference when the external_service is undefined and sets external_service to null', () => { - const caseSavedObject = create_7_14_0_case(); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); + it('does not create a reference when the external_service.connector_id is none', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: createExternalService({ connector_id: noneConnectorId }), }); - it('does not create a reference when the external_service.connector_id is none', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: createExternalService({ connector_id: noneConnectorId }), - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('preserves the existing references when migrating', () => { + const caseSavedObject = { + ...create_7_14_0_case(), + references: [{ id: '1', name: 'awesome', type: 'hello' }], + }; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); + "id": "1", + "name": "awesome", + "type": "hello", + }, + ] + `); + }); - it('preserves the existing references when migrating', () => { - const caseSavedObject = { - ...create_7_14_0_case(), - references: [{ id: '1', name: 'awesome', type: 'hello' }], - }; - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "awesome", - "type": "hello", - }, - ] - `); + it('creates a connector reference and removes the connector.id field', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }, }); - it('creates a connector reference and removes the connector.id field', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + ] + `); + }); - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", - }, - ] - `); + it('creates a push connector reference and removes the connector_id field', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, }); - it('creates a push connector reference and removes the connector_id field', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: null, - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, + "id": "100", + "name": "pushConnectorId", + "type": "action", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + ] + `); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); + it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: null, + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, }); - it('migrates both connector and external_service when provided', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", }, - }); + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('migrates both connector and external_service when provided', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }, + }); - expect(migratedConnector.references.length).toBe(2); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(2); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + "id": "123", + "name": "connectorId", + "type": "action", + }, Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", - }, - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index 80f02fa3bf6a..bffd4171270e 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -14,11 +14,7 @@ import { } from '../../../../../../src/core/server'; import { ESConnectorFields } from '../../services'; import { ConnectorTypes, CaseType } from '../../../common'; -import { - transformConnectorIdToReference, - transformPushConnectorIdToReference, -} from '../../services/user_actions/transform'; -import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../../common'; +import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; interface UnsanitizedCaseConnector { connector_id: string; @@ -54,13 +50,11 @@ export const caseConnectorIdMigration = ( // removing the id field since it will be stored in the references instead const { connector, external_service, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - connector - ); + const { transformedConnector, references: connectorReferences } = + transformConnectorIdToReference(connector); const { transformedPushConnector, references: pushConnectorReferences } = - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, external_service); + transformPushConnectorIdToReference(external_service); const { references = [] } = doc; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index 9ae0285598db..4467b499817a 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -40,89 +40,87 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); -describe('configuration migrations', () => { - describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector ID is none', () => { - const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); +describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector ID is none', () => { + const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('creates a reference using the connector id', () => { - const configureSavedObject = create_7_14_0_configSchema({ - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }); + it('creates a reference using the connector id', () => { + const configureSavedObject = create_7_14_0_configSchema({ + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references).toEqual([ - { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, - ]); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references).toEqual([ + { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, + ]); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('returns the other attributes and default connector when the connector is undefined', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('returns the other attributes and default connector when the connector is undefined', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "closure_type": "close-by-pushing", - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "owner": "securitySolution", - "updated_at": "2020-04-09T09:43:51.778Z", - "updated_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, + expect(migratedConnector).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "closure_type": "close-by-pushing", + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", }, - "id": "1", - "references": Array [], - "type": "cases-configure", - } - `); - }); + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + "owner": "securitySolution", + "updated_at": "2020-04-09T09:43:51.778Z", + "updated_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + "id": "1", + "references": Array [], + "type": "cases-configure", + } + `); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index f9937253e0d2..527d40fca2e3 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -13,8 +13,7 @@ import { } from '../../../../../../src/core/server'; import { ConnectorTypes } from '../../../common'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { transformConnectorIdToReference } from '../../services/user_actions/transform'; -import { CONNECTOR_ID_REFERENCE_NAME } from '../../common'; +import { transformConnectorIdToReference } from './utils'; interface UnsanitizedConfigureConnector { connector_id: string; @@ -35,10 +34,8 @@ export const configureConnectorIdMigration = ( ): SavedObjectSanitizedDoc => { // removing the id field since it will be stored in the references instead const { connector, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - connector - ); + const { transformedConnector, references: connectorReferences } = + transformConnectorIdToReference(connector); const { references = [] } = doc; return { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts index a4f50fbfcde5..a445131073d1 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts @@ -5,17 +5,24 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, } from '../../../../../../src/core/server'; -import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; export { caseMigrations } from './cases'; export { configureMigrations } from './configuration'; -export { userActionsMigrations } from './user_actions'; export { createCommentsMigrations, CreateCommentsMigrationsDeps } from './comments'; +interface UserActions { + action_field: string[]; + new_value: string; + old_value: string; +} + export interface SanitizedCaseOwner { owner: string; } @@ -31,6 +38,52 @@ export const addOwnerToSO = >( references: doc.references || [], }); +export const userActionsMigrations = { + '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { + const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; + + if ( + action_field == null || + !Array.isArray(action_field) || + action_field[0] !== 'connector_id' + ) { + return { ...doc, references: doc.references || [] }; + } + + return { + ...doc, + attributes: { + ...restAttributes, + action_field: ['connector'], + new_value: + new_value != null + ? JSON.stringify({ + id: new_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : new_value, + old_value: + old_value != null + ? JSON.stringify({ + id: old_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : old_value, + }, + references: doc.references || [], + }; + }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc> + ): SavedObjectSanitizedDoc => { + return addOwnerToSO(doc); + }, +}; + export const connectorMappingsMigrations = { '7.14.0': ( doc: SavedObjectUnsanitizedDoc> diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts deleted file mode 100644 index e71c8db0db69..000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts +++ /dev/null @@ -1,562 +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. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { SavedObjectMigrationContext, SavedObjectSanitizedDoc } from 'kibana/server'; -import { migrationMocks } from 'src/core/server/mocks'; -import { CaseUserActionAttributes, CASE_USER_ACTION_SAVED_OBJECT } from '../../../common'; -import { - createConnectorObject, - createExternalService, - createJiraConnector, -} from '../../services/test_utils'; -import { userActionsConnectorIdMigration } from './user_actions'; - -const create_7_14_0_userAction = ( - params: { - action?: string; - action_field?: string[]; - new_value?: string | null | object; - old_value?: string | null | object; - } = {} -) => { - const { new_value, old_value, ...restParams } = params; - - return { - type: CASE_USER_ACTION_SAVED_OBJECT, - id: '1', - attributes: { - ...restParams, - new_value: new_value && typeof new_value === 'object' ? JSON.stringify(new_value) : new_value, - old_value: old_value && typeof old_value === 'object' ? JSON.stringify(old_value) : old_value, - }, - }; -}; - -describe('user action migrations', () => { - describe('7.15.0 connector ID migration', () => { - describe('userActionsConnectorIdMigration', () => { - let context: jest.Mocked; - - beforeEach(() => { - context = migrationMocks.createContext(); - }); - - describe('push user action', () => { - it('extracts the external_service connector_id to references for a new pushed user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedExternalService).not.toHaveProperty('connector_id'); - expect(parsedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extract the external_service connector_id to references for new and old pushed user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: createExternalService({ connector_id: '5' }), - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(migratedUserAction.references).toEqual([ - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the external_service connector_id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: createExternalService({ connector_id: '5' }), - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid push user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['invalid field'], - new_value: 'hello', - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction.attributes.old_value).toBeNull(); - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": null, - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when it new value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: '{a', - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction.attributes.old_value).toBeNull(); - expect(migratedUserAction.attributes.new_value).toEqual('{a'); - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_field": Array [ - "pushed", - ], - "new_value": "{a", - "old_value": null, - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error new value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: '{a', - old_value: null, - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - - describe('update connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedConnector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: { ...createJiraConnector(), id: '5' }, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); - - expect(parsedNewConnector).not.toHaveProperty('id'); - expect(parsedOldConnector).not.toHaveProperty('id'); - - expect(migratedUserAction.references).toEqual([ - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the connector.id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: { ...createJiraConnector(), id: '5' }, - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewConnectorId).not.toHaveProperty('id'); - expect(parsedOldConnectorId).not.toHaveProperty('id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['invalid action'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when old_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: '{}', - old_value: '{b', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_field": Array [ - "connector", - ], - "new_value": "{}", - "old_value": "{b", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error message when old_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: '{b', - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - - describe('create connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedConnector.connector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: createConnectorObject({ id: '5' }), - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); - - expect(parsedNewConnector.connector).not.toHaveProperty('id'); - expect(parsedOldConnector.connector).not.toHaveProperty('id'); - - expect(migratedUserAction.references).toEqual([ - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the connector.id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: createConnectorObject({ id: '5' }), - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewConnectorId.connector).not.toHaveProperty('id'); - expect(parsedOldConnectorId.connector).not.toHaveProperty('id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['invalid action'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when new_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_field": Array [ - "connector", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error message when new_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: 'new json value', - old_value: 'old value', - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts deleted file mode 100644 index ed6b57ef647f..000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts +++ /dev/null @@ -1,159 +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. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { - SavedObjectUnsanitizedDoc, - SavedObjectSanitizedDoc, - SavedObjectMigrationContext, - LogMeta, -} from '../../../../../../src/core/server'; -import { ConnectorTypes, isCreateConnector, isPush, isUpdateConnector } from '../../../common'; - -import { extractConnectorIdFromJson } from '../../services/user_actions/transform'; -import { UserActionFieldType } from '../../services/user_actions/types'; - -interface UserActions { - action_field: string[]; - new_value: string; - old_value: string; -} - -interface UserActionUnmigratedConnectorDocument { - action?: string; - action_field?: string[]; - new_value?: string | null; - old_value?: string | null; -} - -interface UserActionLogMeta extends LogMeta { - migrations: { userAction: { id: string } }; -} - -export function userActionsConnectorIdMigration( - doc: SavedObjectUnsanitizedDoc, - context: SavedObjectMigrationContext -): SavedObjectSanitizedDoc { - const originalDocWithReferences = { ...doc, references: doc.references ?? [] }; - - if (!isConnectorUserAction(doc.attributes.action, doc.attributes.action_field)) { - return originalDocWithReferences; - } - - try { - return formatDocumentWithConnectorReferences(doc); - } catch (error) { - logError(doc.id, context, error); - - return originalDocWithReferences; - } -} - -function isConnectorUserAction(action?: string, actionFields?: string[]): boolean { - return ( - isCreateConnector(action, actionFields) || - isUpdateConnector(action, actionFields) || - isPush(action, actionFields) - ); -} - -function formatDocumentWithConnectorReferences( - doc: SavedObjectUnsanitizedDoc -): SavedObjectSanitizedDoc { - const { new_value, old_value, action, action_field, ...restAttributes } = doc.attributes; - const { references = [] } = doc; - - const { transformedActionDetails: transformedNewValue, references: newValueConnectorRefs } = - extractConnectorIdFromJson({ - action, - actionFields: action_field, - actionDetails: new_value, - fieldType: UserActionFieldType.New, - }); - - const { transformedActionDetails: transformedOldValue, references: oldValueConnectorRefs } = - extractConnectorIdFromJson({ - action, - actionFields: action_field, - actionDetails: old_value, - fieldType: UserActionFieldType.Old, - }); - - return { - ...doc, - attributes: { - ...restAttributes, - action, - action_field, - new_value: transformedNewValue, - old_value: transformedOldValue, - }, - references: [...references, ...newValueConnectorRefs, ...oldValueConnectorRefs], - }; -} - -function logError(id: string, context: SavedObjectMigrationContext, error: Error) { - context.log.error( - `Failed to migrate user action connector doc id: ${id} version: ${context.migrationVersion} error: ${error.message}`, - { - migrations: { - userAction: { - id, - }, - }, - } - ); -} - -export const userActionsMigrations = { - '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { - const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; - - if ( - action_field == null || - !Array.isArray(action_field) || - action_field[0] !== 'connector_id' - ) { - return { ...doc, references: doc.references || [] }; - } - - return { - ...doc, - attributes: { - ...restAttributes, - action_field: ['connector'], - new_value: - new_value != null - ? JSON.stringify({ - id: new_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : new_value, - old_value: - old_value != null - ? JSON.stringify({ - id: old_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : old_value, - }, - references: doc.references || [], - }; - }, - '7.14.0': ( - doc: SavedObjectUnsanitizedDoc> - ): SavedObjectSanitizedDoc => { - return addOwnerToSO(doc); - }, - '7.16.0': userActionsConnectorIdMigration, -}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts new file mode 100644 index 000000000000..f591bef6b323 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { noneConnectorId } from '../../../common'; +import { createExternalService, createJiraConnector } from '../../services/test_utils'; +import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; + +describe('migration utils', () => { + describe('transformConnectorIdToReference', () => { + it('returns the default none connector when the connector is undefined', () => { + expect(transformConnectorIdToReference().transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is undefined', () => { + expect(transformConnectorIdToReference({ id: undefined }).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none', () => { + expect(transformConnectorIdToReference({ id: noneConnectorId }).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none and other fields are defined', () => { + expect( + transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns an empty array of references when the connector is undefined', () => { + expect(transformConnectorIdToReference().references.length).toBe(0); + }); + + it('returns an empty array of references when the id is undefined', () => { + expect(transformConnectorIdToReference({ id: undefined }).references.length).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector', () => { + expect(transformConnectorIdToReference({ id: noneConnectorId }).references.length).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector and other fields are defined', () => { + expect( + transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) + .references.length + ).toBe(0); + }); + + it('returns a jira connector', () => { + const transformedFields = transformConnectorIdToReference(createJiraConnector()); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('transformPushConnectorIdToReference', () => { + it('sets external_service to null when it is undefined', () => { + expect(transformPushConnectorIdToReference().transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('sets external_service to null when it is null', () => { + expect(transformPushConnectorIdToReference(null).transformedPushConnector) + .toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is null', () => { + expect(transformPushConnectorIdToReference({ connector_id: null }).transformedPushConnector) + .toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is none', () => { + const otherFields = { otherField: 'hi' }; + + expect( + transformPushConnectorIdToReference({ ...otherFields, connector_id: noneConnectorId }) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "otherField": "hi", + }, + } + `); + }); + + it('returns an empty array of references when the external_service is undefined', () => { + expect(transformPushConnectorIdToReference().references.length).toBe(0); + }); + + it('returns an empty array of references when the external_service is null', () => { + expect(transformPushConnectorIdToReference(null).references.length).toBe(0); + }); + + it('returns an empty array of references when the connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is null', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector', () => { + expect( + transformPushConnectorIdToReference({ connector_id: noneConnectorId }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { + expect( + transformPushConnectorIdToReference({ + ...createExternalService(), + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns the external_service connector', () => { + const transformedFields = transformPushConnectorIdToReference(createExternalService()); + expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts new file mode 100644 index 000000000000..0100a04cde67 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { noneConnectorId } from '../../../common'; +import { SavedObjectReference } from '../../../../../../src/core/server'; +import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; +import { + getNoneCaseConnector, + CONNECTOR_ID_REFERENCE_NAME, + PUSH_CONNECTOR_ID_REFERENCE_NAME, +} from '../../common'; + +export const transformConnectorIdToReference = (connector?: { + id?: string; +}): { transformedConnector: Record; references: SavedObjectReference[] } => { + const { id: connectorId, ...restConnector } = connector ?? {}; + + const references = createConnectorReference( + connectorId, + ACTION_SAVED_OBJECT_TYPE, + CONNECTOR_ID_REFERENCE_NAME + ); + + const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); + const connectorFieldsToReturn = + connector && references.length > 0 ? restConnector : restNoneConnector; + + return { + transformedConnector: { + connector: connectorFieldsToReturn, + }, + references, + }; +}; + +const createConnectorReference = ( + id: string | null | undefined, + type: string, + name: string +): SavedObjectReference[] => { + return id && id !== noneConnectorId + ? [ + { + id, + type, + name, + }, + ] + : []; +}; + +export const transformPushConnectorIdToReference = ( + external_service?: { connector_id?: string | null } | null +): { transformedPushConnector: Record; references: SavedObjectReference[] } => { + const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; + + const references = createConnectorReference( + pushConnectorId, + ACTION_SAVED_OBJECT_TYPE, + PUSH_CONNECTOR_ID_REFERENCE_NAME + ); + + return { + transformedPushConnector: { external_service: external_service ? restExternalService : null }, + references, + }; +}; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 8c71abe5bff4..18f4ff867cfa 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -40,7 +40,6 @@ import { createSavedObjectReferences, createCaseSavedObjectResponse, basicCaseFields, - createSOFindResponse, } from '../test_utils'; import { ESCaseAttributes } from './types'; @@ -88,6 +87,13 @@ const createFindSO = ( score: 0, }); +const createSOFindResponse = (savedObjects: Array>) => ({ + saved_objects: savedObjects, + total: savedObjects.length, + per_page: savedObjects.length, + page: 1, +}); + const createCaseUpdateParams = ( connector?: CaseConnector, externalService?: CaseFullExternalService diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index 07743eda6121..b712ea07f9c7 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference, SavedObjectsFindResult } from 'kibana/server'; +import { SavedObject, SavedObjectReference } from 'kibana/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common'; import { @@ -54,7 +54,7 @@ export const createESJiraConnector = ( { key: 'parent', value: '2' }, ], type: ConnectorTypes.jira, - ...overrides, + ...(overrides && { ...overrides }), }; }; @@ -94,7 +94,7 @@ export const createExternalService = ( email: 'testemail@elastic.co', username: 'elastic', }, - ...overrides, + ...(overrides && { ...overrides }), }); export const basicCaseFields = { @@ -198,14 +198,3 @@ export const createSavedObjectReferences = ({ ] : []), ]; - -export const createConnectorObject = (overrides?: Partial) => ({ - connector: { ...createJiraConnector(), ...overrides }, -}); - -export const createSOFindResponse = (savedObjects: Array>) => ({ - saved_objects: savedObjects, - total: savedObjects.length, - per_page: savedObjects.length, - page: 1, -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts deleted file mode 100644 index 7bcbaf58d0f6..000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts +++ /dev/null @@ -1,332 +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 { UserActionField } from '../../../common'; -import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; -import { buildCaseUserActionItem } from './helpers'; - -const defaultFields = () => ({ - actionAt: 'now', - actionBy: { - email: 'a', - full_name: 'j', - username: '1', - }, - caseId: '300', - owner: 'securitySolution', -}); - -describe('user action helpers', () => { - describe('buildCaseUserActionItem', () => { - describe('push user action', () => { - it('extracts the external_service connector_id to references for a new pushed user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - }); - - const parsedExternalService = JSON.parse(userAction.attributes.new_value!); - expect(parsedExternalService).not.toHaveProperty('connector_id'); - expect(parsedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extract the external_service connector_id to references for new and old pushed user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - oldValue: createExternalService({ connector_id: '5' }), - }); - - const parsedNewExternalService = JSON.parse(userAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(userAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid push user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['invalid field'] as unknown as UserActionField, - newValue: 'hello' as unknown as Record, - }); - - expect(userAction.attributes.old_value).toBeNull(); - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": null, - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - - describe('update connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - }); - - const parsedConnector = JSON.parse(userAction.attributes.new_value!); - expect(parsedConnector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - oldValue: { ...createJiraConnector(), id: '5' }, - }); - - const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); - - expect(parsedNewConnector).not.toHaveProperty('id'); - expect(parsedOldConnector).not.toHaveProperty('id'); - - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['invalid field'] as unknown as UserActionField, - newValue: 'hello' as unknown as Record, - oldValue: 'old value' as unknown as Record, - }); - - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": "old value", - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - - describe('create connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - }); - - const parsedConnector = JSON.parse(userAction.attributes.new_value!); - expect(parsedConnector.connector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - oldValue: createConnectorObject({ id: '5' }), - }); - - const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); - - expect(parsedNewConnector.connector).not.toHaveProperty('id'); - expect(parsedOldConnector.connector).not.toHaveProperty('id'); - - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['invalid action'] as unknown as UserActionField, - newValue: 'new json value' as unknown as Record, - oldValue: 'old value' as unknown as Record, - }); - - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index e91b69f0995b..223e731aa8d9 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference, SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObject, SavedObjectsUpdateResponse } from 'kibana/server'; import { get, isPlainObject, isString } from 'lodash'; import deepEqual from 'fast-deep-equal'; @@ -23,68 +23,8 @@ import { } from '../../../common'; import { isTwoArraysDifference } from '../../client/utils'; import { UserActionItem } from '.'; -import { extractConnectorId } from './transform'; -import { UserActionFieldType } from './types'; -import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -interface BuildCaseUserActionParams { - action: UserAction; - actionAt: string; - actionBy: User; - caseId: string; - owner: string; - fields: UserActionField; - newValue?: Record | string | null; - oldValue?: Record | string | null; - subCaseId?: string; -} - -export const buildCaseUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCaseUserActionParams): UserActionItem => { - const { transformedActionDetails: transformedNewValue, references: newValueReferences } = - extractConnectorId({ - action, - actionFields: fields, - actionDetails: newValue, - fieldType: UserActionFieldType.New, - }); - - const { transformedActionDetails: transformedOldValue, references: oldValueReferences } = - extractConnectorId({ - action, - actionFields: fields, - actionDetails: oldValue, - fieldType: UserActionFieldType.Old, - }); - - return { - attributes: transformNewUserAction({ - actionField: fields, - action, - actionAt, - owner, - ...actionBy, - newValue: transformedNewValue, - oldValue: transformedOldValue, - }), - references: [ - ...createCaseReferences(caseId, subCaseId), - ...newValueReferences, - ...oldValueReferences, - ], - }; -}; - -const transformNewUserAction = ({ +export const transformNewUserAction = ({ actionField, action, actionAt, @@ -115,43 +55,103 @@ const transformNewUserAction = ({ owner, }); -const createCaseReferences = (caseId: string, subCaseId?: string): SavedObjectReference[] => [ - { - type: CASE_SAVED_OBJECT, - name: CASE_REF_NAME, - id: caseId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - name: SUB_CASE_REF_NAME, - id: subCaseId, - }, - ] - : []), -]; +interface BuildCaseUserAction { + action: UserAction; + actionAt: string; + actionBy: User; + caseId: string; + owner: string; + fields: UserActionField | unknown[]; + newValue?: string | unknown; + oldValue?: string | unknown; + subCaseId?: string; +} -interface BuildCommentUserActionItem extends BuildCaseUserActionParams { +interface BuildCommentUserActionItem extends BuildCaseUserAction { commentId: string; } -export const buildCommentUserActionItem = (params: BuildCommentUserActionItem): UserActionItem => { - const { commentId } = params; - const { attributes, references } = buildCaseUserActionItem(params); +export const buildCommentUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + commentId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCommentUserActionItem): UserActionItem => ({ + attributes: transformNewUserAction({ + actionField: fields as UserActionField, + action, + actionAt, + owner, + ...actionBy, + newValue: newValue as string, + oldValue: oldValue as string, + }), + references: [ + { + type: CASE_SAVED_OBJECT, + name: `associated-${CASE_SAVED_OBJECT}`, + id: caseId, + }, + { + type: CASE_COMMENT_SAVED_OBJECT, + name: `associated-${CASE_COMMENT_SAVED_OBJECT}`, + id: commentId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + id: subCaseId, + name: `associated-${SUB_CASE_SAVED_OBJECT}`, + }, + ] + : []), + ], +}); - return { - attributes, - references: [ - ...references, - { - type: CASE_COMMENT_SAVED_OBJECT, - name: COMMENT_REF_NAME, - id: commentId, - }, - ], - }; -}; +export const buildCaseUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCaseUserAction): UserActionItem => ({ + attributes: transformNewUserAction({ + actionField: fields as UserActionField, + action, + actionAt, + owner, + ...actionBy, + newValue: newValue as string, + oldValue: oldValue as string, + }), + references: [ + { + type: CASE_SAVED_OBJECT, + name: `associated-${CASE_SAVED_OBJECT}`, + id: caseId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + name: `associated-${SUB_CASE_SAVED_OBJECT}`, + id: subCaseId, + }, + ] + : []), + ], +}); const userActionFieldsAllowed: UserActionField = [ 'comment', @@ -278,8 +278,8 @@ const buildGenericCaseUserActions = ({ caseId, subCaseId, fields: [field], - newValue: updatedValue, - oldValue: origValue, + newValue: JSON.stringify(updatedValue), + oldValue: JSON.stringify(origValue), owner: originalItem.attributes.owner, }), ]; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts deleted file mode 100644 index c4a350f4ac01..000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/index.test.ts +++ /dev/null @@ -1,557 +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 { SavedObject, SavedObjectsFindResult } from 'kibana/server'; -import { transformFindResponseToExternalModel, UserActionItem } from '.'; -import { - CaseUserActionAttributes, - CASE_USER_ACTION_SAVED_OBJECT, - UserAction, - UserActionField, -} from '../../../common'; - -import { - createConnectorObject, - createExternalService, - createJiraConnector, - createSOFindResponse, -} from '../test_utils'; -import { buildCaseUserActionItem, buildCommentUserActionItem } from './helpers'; - -const createConnectorUserAction = ( - subCaseId?: string, - overrides?: Partial -): SavedObject => { - return { - ...createUserActionSO({ - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const updateConnectorUserAction = ({ - subCaseId, - overrides, - oldValue, -}: { - subCaseId?: string; - overrides?: Partial; - oldValue?: string | null | Record; -} = {}): SavedObject => { - return { - ...createUserActionSO({ - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - oldValue, - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const pushConnectorUserAction = ({ - subCaseId, - overrides, - oldValue, -}: { - subCaseId?: string; - overrides?: Partial; - oldValue?: string | null | Record; -} = {}): SavedObject => { - return { - ...createUserActionSO({ - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - oldValue, - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const createUserActionFindSO = ( - userAction: SavedObject -): SavedObjectsFindResult => ({ - ...userAction, - score: 0, -}); - -const createUserActionSO = ({ - action, - fields, - subCaseId, - newValue, - oldValue, - attributesOverrides, - commentId, -}: { - action: UserAction; - fields: UserActionField; - subCaseId?: string; - newValue?: string | null | Record; - oldValue?: string | null | Record; - attributesOverrides?: Partial; - commentId?: string; -}): SavedObject => { - const defaultParams = { - action, - actionAt: 'abc', - actionBy: { - email: 'a', - username: 'b', - full_name: 'abc', - }, - caseId: '1', - subCaseId, - fields, - newValue, - oldValue, - owner: 'securitySolution', - }; - - let userAction: UserActionItem; - - if (commentId) { - userAction = buildCommentUserActionItem({ - commentId, - ...defaultParams, - }); - } else { - userAction = buildCaseUserActionItem(defaultParams); - } - - return { - type: CASE_USER_ACTION_SAVED_OBJECT, - id: '100', - attributes: { - ...userAction.attributes, - ...(attributesOverrides && { ...attributesOverrides }), - }, - references: userAction.references, - }; -}; - -describe('CaseUserActionService', () => { - describe('transformFindResponseToExternalModel', () => { - it('does not populate the ids when the response is an empty array', () => { - expect(transformFindResponseToExternalModel(createSOFindResponse([]))).toMatchInlineSnapshot(` - Object { - "page": 1, - "per_page": 0, - "saved_objects": Array [], - "total": 0, - } - `); - }); - - it('preserves the saved object fields and attributes when inject the ids', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(createConnectorUserAction())]) - ); - - expect(transformed).toMatchInlineSnapshot(` - Object { - "page": 1, - "per_page": 1, - "saved_objects": Array [ - Object { - "attributes": Object { - "action": "create", - "action_at": "abc", - "action_by": Object { - "email": "a", - "full_name": "abc", - "username": "b", - }, - "action_field": Array [ - "connector", - ], - "action_id": "100", - "case_id": "1", - "comment_id": null, - "new_val_connector_id": "1", - "new_value": "{\\"connector\\":{\\"name\\":\\".jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"bug\\",\\"priority\\":\\"high\\",\\"parent\\":\\"2\\"}}}", - "old_val_connector_id": null, - "old_value": null, - "owner": "securitySolution", - "sub_case_id": "", - }, - "id": "100", - "references": Array [ - Object { - "id": "1", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ], - "score": 0, - "type": "cases-user-actions", - }, - ], - "total": 1, - } - `); - }); - - it('populates the new_val_connector_id for multiple user actions', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(createConnectorUserAction()), - createUserActionFindSO(createConnectorUserAction()), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - expect(transformed.saved_objects[1].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id for multiple user actions', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO( - createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }) - ), - createUserActionFindSO( - createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject({ id: '10' }), - }) - ), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - expect(transformed.saved_objects[1].attributes.old_val_connector_id).toEqual('10'); - }); - - describe('reference ids', () => { - it('sets case_id to an empty string when it cannot find the reference', () => { - const userAction = { - ...createConnectorUserAction(), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.case_id).toEqual(''); - }); - - it('sets comment_id to null when it cannot find the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], commentId: '5' }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); - }); - - it('sets sub_case_id to an empty string when it cannot find the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); - }); - - it('sets case_id correctly when it finds the reference', () => { - const userAction = createConnectorUserAction(); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.case_id).toEqual('1'); - }); - - it('sets comment_id correctly when it finds the reference', () => { - const userAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - commentId: '5', - }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toEqual('5'); - }); - - it('sets sub_case_id correctly when it finds the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - }; - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.sub_case_id).toEqual('5'); - }); - - it('sets action_id correctly to the saved object id', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - }; - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.action_id).toEqual('100'); - }); - }); - - describe('create connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...createConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { ...createConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = createConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = createConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - }); - }); - - describe('update connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...updateConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { - ...updateConnectorUserAction({ oldValue: createJiraConnector() }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = updateConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = updateConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - }); - }); - - describe('push connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...pushConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { - ...pushConnectorUserAction({ oldValue: createExternalService() }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = pushConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = pushConnectorUserAction({ oldValue: createExternalService() }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = pushConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('100'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = pushConnectorUserAction({ oldValue: createExternalService() }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('100'); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index 4f158862e3d6..b70244816555 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,12 +5,7 @@ * 2.0. */ -import { - Logger, - SavedObjectReference, - SavedObjectsFindResponse, - SavedObjectsFindResult, -} from 'kibana/server'; +import { Logger, SavedObjectReference } from 'kibana/server'; import { CASE_SAVED_OBJECT, @@ -18,17 +13,8 @@ import { CaseUserActionAttributes, MAX_DOCS_PER_PAGE, SUB_CASE_SAVED_OBJECT, - CaseUserActionResponse, - CASE_COMMENT_SAVED_OBJECT, - isCreateConnector, - isPush, - isUpdateConnector, } from '../../../common'; import { ClientArgs } from '..'; -import { UserActionFieldType } from './types'; -import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -import { ConnectorIdReferenceName, PushConnectorIdReferenceName } from './transform'; -import { findConnectorIdReference } from '../transform'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -47,16 +33,12 @@ interface PostCaseUserActionArgs extends ClientArgs { export class CaseUserActionService { constructor(private readonly log: Logger) {} - public async getAll({ - unsecuredSavedObjectsClient, - caseId, - subCaseId, - }: GetCaseUserActionArgs): Promise> { + public async getAll({ unsecuredSavedObjectsClient, caseId, subCaseId }: GetCaseUserActionArgs) { try { const id = subCaseId ?? caseId; const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT; - const userActions = await unsecuredSavedObjectsClient.find({ + return await unsecuredSavedObjectsClient.find({ type: CASE_USER_ACTION_SAVED_OBJECT, hasReference: { type, id }, page: 1, @@ -64,22 +46,17 @@ export class CaseUserActionService { sortField: 'action_at', sortOrder: 'asc', }); - - return transformFindResponseToExternalModel(userActions); } catch (error) { this.log.error(`Error on GET case user action case id: ${caseId}: ${error}`); throw error; } } - public async bulkCreate({ - unsecuredSavedObjectsClient, - actions, - }: PostCaseUserActionArgs): Promise { + public async bulkCreate({ unsecuredSavedObjectsClient, actions }: PostCaseUserActionArgs) { try { this.log.debug(`Attempting to POST a new case user action`); - await unsecuredSavedObjectsClient.bulkCreate( + return await unsecuredSavedObjectsClient.bulkCreate( actions.map((action) => ({ type: CASE_USER_ACTION_SAVED_OBJECT, ...action })) ); } catch (error) { @@ -88,71 +65,3 @@ export class CaseUserActionService { } } } - -export function transformFindResponseToExternalModel( - userActions: SavedObjectsFindResponse -): SavedObjectsFindResponse { - return { - ...userActions, - saved_objects: userActions.saved_objects.map((so) => ({ - ...so, - ...transformToExternalModel(so), - })), - }; -} - -function transformToExternalModel( - userAction: SavedObjectsFindResult -): SavedObjectsFindResult { - const { references } = userAction; - - const newValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.New, userAction); - const oldValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.Old, userAction); - - const caseId = findReferenceId(CASE_REF_NAME, CASE_SAVED_OBJECT, references) ?? ''; - const commentId = - findReferenceId(COMMENT_REF_NAME, CASE_COMMENT_SAVED_OBJECT, references) ?? null; - const subCaseId = findReferenceId(SUB_CASE_REF_NAME, SUB_CASE_SAVED_OBJECT, references) ?? ''; - - return { - ...userAction, - attributes: { - ...userAction.attributes, - action_id: userAction.id, - case_id: caseId, - comment_id: commentId, - sub_case_id: subCaseId, - new_val_connector_id: newValueConnectorId, - old_val_connector_id: oldValueConnectorId, - }, - }; -} - -function getConnectorIdFromReferences( - fieldType: UserActionFieldType, - userAction: SavedObjectsFindResult -): string | null { - const { - // eslint-disable-next-line @typescript-eslint/naming-convention - attributes: { action, action_field }, - references, - } = userAction; - - if (isCreateConnector(action, action_field) || isUpdateConnector(action, action_field)) { - return findConnectorIdReference(ConnectorIdReferenceName[fieldType], references)?.id ?? null; - } else if (isPush(action, action_field)) { - return ( - findConnectorIdReference(PushConnectorIdReferenceName[fieldType], references)?.id ?? null - ); - } - - return null; -} - -function findReferenceId( - name: string, - type: string, - references: SavedObjectReference[] -): string | undefined { - return references.find((ref) => ref.name === name && ref.type === type)?.id; -} diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts deleted file mode 100644 index 2d2877061709..000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts +++ /dev/null @@ -1,1246 +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 { noneConnectorId } from '../../../common'; -import { - CONNECTOR_ID_REFERENCE_NAME, - getNoneCaseConnector, - PUSH_CONNECTOR_ID_REFERENCE_NAME, - USER_ACTION_OLD_ID_REF_NAME, - USER_ACTION_OLD_PUSH_ID_REF_NAME, -} from '../../common'; -import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; -import { - extractConnectorIdHelper, - extractConnectorIdFromJson, - extractConnectorId, - transformConnectorIdToReference, - transformPushConnectorIdToReference, -} from './transform'; -import { UserActionFieldType } from './types'; - -describe('user action transform utils', () => { - describe('transformConnectorIdToReference', () => { - it('returns the default none connector when the connector is undefined', () => { - expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is undefined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none and other fields are defined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { - ...createJiraConnector(), - id: noneConnectorId, - }).transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns an empty array of references when the connector is undefined', () => { - expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).references.length).toBe( - 0 - ); - }); - - it('returns an empty array of references when the id is undefined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }).references - .length - ).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) - .references.length - ).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector and other fields are defined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { - ...createJiraConnector(), - id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns a jira connector', () => { - const transformedFields = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - createJiraConnector() - ); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('returns a jira connector with the user action reference name', () => { - const transformedFields = transformConnectorIdToReference( - USER_ACTION_OLD_ID_REF_NAME, - createJiraConnector() - ); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('transformPushConnectorIdToReference', () => { - it('sets external_service to null when it is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('sets external_service to null when it is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: null, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is none', () => { - const otherFields = { otherField: 'hi' }; - - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - ...otherFields, - connector_id: noneConnectorId, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "otherField": "hi", - }, - } - `); - }); - - it('returns an empty array of references when the external_service is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the external_service is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null).references - .length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - ...createExternalService(), - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns the external_service connector', () => { - const transformedFields = transformPushConnectorIdToReference( - PUSH_CONNECTOR_ID_REFERENCE_NAME, - createExternalService() - ); - expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns the external_service connector with a user actions reference name', () => { - const transformedFields = transformPushConnectorIdToReference( - USER_ACTION_OLD_PUSH_ID_REF_NAME, - createExternalService() - ); - - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('extractConnectorIdHelper', () => { - it('throws an error when action details has a circular reference', () => { - const circularRef = { prop: {} }; - circularRef.prop = circularRef; - - expect(() => { - extractConnectorIdHelper({ - action: 'a', - actionFields: [], - actionDetails: circularRef, - fieldType: UserActionFieldType.New, - }); - }).toThrow(); - }); - - describe('create action', () => { - it('returns no references and untransformed json when actionDetails is not a valid connector', () => { - expect( - extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is create and action fields does not contain connector', () => { - expect( - extractConnectorIdHelper({ - action: 'create', - actionFields: ['', 'something', 'onnector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns the stringified json without the id', () => { - const jiraConnector = createConnectorObject(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails)).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - }); - - it('removes the connector.id when the connector is none', () => { - const connector = { connector: getNoneCaseConnector() }; - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - const parsedJson = JSON.parse(transformedActionDetails); - - expect(parsedJson.connector).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('does not return a reference when the connector is none', () => { - const connector = { connector: getNoneCaseConnector() }; - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toEqual([]); - }); - - it('returns a reference to the connector.id', () => { - const connector = createConnectorObject(); - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('returns an old reference name to the connector.id', () => { - const connector = createConnectorObject(); - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns the transformed connector and the description', () => { - const details = { ...createConnectorObject(), description: 'a description' }; - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: details, - fieldType: UserActionFieldType.Old, - })!; - - const parsedJson = JSON.parse(transformedActionDetails); - - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - "description": "a description", - } - `); - }); - }); - - describe('update action', () => { - it('returns no references and untransformed json when actionDetails is not a valid connector', () => { - expect( - extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is update and action fields does not contain connector', () => { - expect( - extractConnectorIdHelper({ - action: 'update', - actionFields: ['', 'something', 'onnector'], - actionDetails: 5, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "5", - } - `); - }); - - it('returns the stringified json without the id', () => { - const jiraConnector = createJiraConnector(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails!); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - }); - - it('returns the stringified json without the id when the connector is none', () => { - const connector = getNoneCaseConnector(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('does not return a reference when the connector is none', () => { - const connector = getNoneCaseConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toEqual([]); - }); - - it('returns an old reference name to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns no references and untransformed json when actionDetails is not a valid external_service', () => { - expect( - extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is push-to-service and action fields does not contain pushed', () => { - expect( - extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['', 'something', 'ushed'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns an old reference name to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); - - describe('extractConnectorId', () => { - it('returns null when the action details has a circular reference', () => { - const circularRef = { prop: {} }; - circularRef.prop = circularRef; - - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: circularRef, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeNull(); - expect(references).toEqual([]); - }); - - describe('fails to extract the id', () => { - it('returns a null transformed action details when it is initially null', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: null, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeNull(); - expect(references).toEqual([]); - }); - - it('returns an undefined transformed action details when it is initially undefined', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: undefined, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeUndefined(); - expect(references).toEqual([]); - }); - - it('returns a json encoded string and empty references when the action is not a valid connector', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails!)).toEqual({ a: 'hello' }); - expect(references).toEqual([]); - }); - - it('returns a json encoded string and empty references when the action details is an invalid object', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: 5 as unknown as Record, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails!).toEqual('5'); - expect(references).toEqual([]); - }); - }); - - describe('create', () => { - it('extracts the connector.id from a new create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'create', - actionFields: ['connector'], - actionDetails: createConnectorObject(), - fieldType: UserActionFieldType.New, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('extracts the connector.id from an old create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'create', - actionFields: ['connector'], - actionDetails: createConnectorObject(), - fieldType: UserActionFieldType.Old, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('update', () => { - it('extracts the connector.id from a new create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'update', - actionFields: ['connector'], - actionDetails: createJiraConnector(), - fieldType: UserActionFieldType.New, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('extracts the connector.id from an old create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'update', - actionFields: ['connector'], - actionDetails: createJiraConnector(), - fieldType: UserActionFieldType.Old, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails!); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns a reference to the old action details connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); - - describe('extractConnectorIdFromJson', () => { - describe('fails to extract the id', () => { - it('returns no references and null transformed json when action is undefined', () => { - expect( - extractConnectorIdFromJson({ - actionFields: [], - actionDetails: undefined, - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionFields is undefined', () => { - expect( - extractConnectorIdFromJson({ action: 'a', fieldType: UserActionFieldType.New }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionDetails is undefined', () => { - expect( - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionDetails is null', () => { - expect( - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - actionDetails: null, - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: null, - references: [], - }); - }); - - it('throws an error when actionDetails is invalid json', () => { - expect(() => - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - actionDetails: '{a', - fieldType: UserActionFieldType.New, - }) - ).toThrow(); - }); - }); - - describe('create action', () => { - it('returns the stringified json without the id', () => { - const jiraConnector = createConnectorObject(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'create', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails!)).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createConnectorObject(); - - const { references } = extractConnectorIdFromJson({ - action: 'create', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('update action', () => { - it('returns the stringified json without the id', () => { - const jiraConnector = createJiraConnector(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'update', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails!); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdFromJson({ - action: 'update', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: JSON.stringify(externalService), - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails!); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdFromJson({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: JSON.stringify(externalService), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts deleted file mode 100644 index 93595374208a..000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/transform.ts +++ /dev/null @@ -1,320 +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. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as rt from 'io-ts'; -import { isString } from 'lodash'; - -import { SavedObjectReference } from '../../../../../../src/core/server'; -import { - CaseAttributes, - CaseConnector, - CaseConnectorRt, - CaseExternalServiceBasicRt, - isCreateConnector, - isPush, - isUpdateConnector, - noneConnectorId, -} from '../../../common'; -import { - CONNECTOR_ID_REFERENCE_NAME, - getNoneCaseConnector, - PUSH_CONNECTOR_ID_REFERENCE_NAME, - USER_ACTION_OLD_ID_REF_NAME, - USER_ACTION_OLD_PUSH_ID_REF_NAME, -} from '../../common'; -import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { UserActionFieldType } from './types'; - -/** - * Extracts the connector id from a json encoded string and formats it as a saved object reference. This will remove - * the field it extracted the connector id from. - */ -export function extractConnectorIdFromJson({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action?: string; - actionFields?: string[]; - actionDetails?: string | null; - fieldType: UserActionFieldType; -}): { transformedActionDetails?: string | null; references: SavedObjectReference[] } { - if (!action || !actionFields || !actionDetails) { - return { transformedActionDetails: actionDetails, references: [] }; - } - - const decodedJson = JSON.parse(actionDetails); - - return extractConnectorIdHelper({ - action, - actionFields, - actionDetails: decodedJson, - fieldType, - }); -} - -/** - * Extracts the connector id from an unencoded object and formats it as a saved object reference. - * This will remove the field it extracted the connector id from. - */ -export function extractConnectorId({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action: string; - actionFields: string[]; - actionDetails?: Record | string | null; - fieldType: UserActionFieldType; -}): { - transformedActionDetails?: string | null; - references: SavedObjectReference[]; -} { - if (!actionDetails || isString(actionDetails)) { - // the action was null, undefined, or a regular string so just return it unmodified and not encoded - return { transformedActionDetails: actionDetails, references: [] }; - } - - try { - return extractConnectorIdHelper({ - action, - actionFields, - actionDetails, - fieldType, - }); - } catch (error) { - return { transformedActionDetails: encodeActionDetails(actionDetails), references: [] }; - } -} - -function encodeActionDetails(actionDetails: Record): string | null { - try { - return JSON.stringify(actionDetails); - } catch (error) { - return null; - } -} - -/** - * Internal helper function for extracting the connector id. This is only exported for usage in unit tests. - * This function handles encoding the transformed fields as a json string - */ -export function extractConnectorIdHelper({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action: string; - actionFields: string[]; - actionDetails: unknown; - fieldType: UserActionFieldType; -}): { transformedActionDetails: string; references: SavedObjectReference[] } { - let transformedActionDetails: unknown = actionDetails; - let referencesToReturn: SavedObjectReference[] = []; - - try { - if (isCreateCaseConnector(action, actionFields, actionDetails)) { - const { transformedActionDetails: transformedConnectorPortion, references } = - transformConnectorFromCreateAndUpdateAction(actionDetails.connector, fieldType); - - // the above call only transforms the connector portion of the action details so let's add back - // the rest of the details and we'll overwrite the connector portion when the transformed one - transformedActionDetails = { - ...actionDetails, - ...transformedConnectorPortion, - }; - referencesToReturn = references; - } else if (isUpdateCaseConnector(action, actionFields, actionDetails)) { - const { - transformedActionDetails: { connector: transformedConnector }, - references, - } = transformConnectorFromCreateAndUpdateAction(actionDetails, fieldType); - - transformedActionDetails = transformedConnector; - referencesToReturn = references; - } else if (isPushConnector(action, actionFields, actionDetails)) { - ({ transformedActionDetails, references: referencesToReturn } = - transformConnectorFromPushAction(actionDetails, fieldType)); - } - } catch (error) { - // ignore any errors, we'll just return whatever was passed in for action details in that case - } - - return { - transformedActionDetails: JSON.stringify(transformedActionDetails), - references: referencesToReturn, - }; -} - -function isCreateCaseConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is { connector: CaseConnector } { - try { - const unsafeCase = actionDetails as CaseAttributes; - - return ( - isCreateConnector(action, actionFields) && - unsafeCase.connector !== undefined && - CaseConnectorRt.is(unsafeCase.connector) - ); - } catch { - return false; - } -} - -export const ConnectorIdReferenceName: Record = { - [UserActionFieldType.New]: CONNECTOR_ID_REFERENCE_NAME, - [UserActionFieldType.Old]: USER_ACTION_OLD_ID_REF_NAME, -}; - -function transformConnectorFromCreateAndUpdateAction( - connector: CaseConnector, - fieldType: UserActionFieldType -): { - transformedActionDetails: { connector: unknown }; - references: SavedObjectReference[]; -} { - const { transformedConnector, references } = transformConnectorIdToReference( - ConnectorIdReferenceName[fieldType], - connector - ); - - return { - transformedActionDetails: transformedConnector, - references, - }; -} - -type ConnectorIdRefNameType = - | typeof CONNECTOR_ID_REFERENCE_NAME - | typeof USER_ACTION_OLD_ID_REF_NAME; - -export const transformConnectorIdToReference = ( - referenceName: ConnectorIdRefNameType, - connector?: { - id?: string; - } -): { - transformedConnector: { connector: unknown }; - references: SavedObjectReference[]; -} => { - const { id: connectorId, ...restConnector } = connector ?? {}; - - const references = createConnectorReference(connectorId, ACTION_SAVED_OBJECT_TYPE, referenceName); - - const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); - const connectorFieldsToReturn = - connector && isConnectorIdValid(connectorId) ? restConnector : restNoneConnector; - - return { - transformedConnector: { - connector: connectorFieldsToReturn, - }, - references, - }; -}; - -const createConnectorReference = ( - id: string | null | undefined, - type: string, - name: string -): SavedObjectReference[] => { - return isConnectorIdValid(id) - ? [ - { - id, - type, - name, - }, - ] - : []; -}; - -const isConnectorIdValid = (id: string | null | undefined): id is string => - id != null && id !== noneConnectorId; - -function isUpdateCaseConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is CaseConnector { - try { - return isUpdateConnector(action, actionFields) && CaseConnectorRt.is(actionDetails); - } catch { - return false; - } -} - -type CaseExternalService = rt.TypeOf; - -function isPushConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is CaseExternalService { - try { - return isPush(action, actionFields) && CaseExternalServiceBasicRt.is(actionDetails); - } catch { - return false; - } -} - -export const PushConnectorIdReferenceName: Record = - { - [UserActionFieldType.New]: PUSH_CONNECTOR_ID_REFERENCE_NAME, - [UserActionFieldType.Old]: USER_ACTION_OLD_PUSH_ID_REF_NAME, - }; - -function transformConnectorFromPushAction( - externalService: CaseExternalService, - fieldType: UserActionFieldType -): { - transformedActionDetails: {} | null; - references: SavedObjectReference[]; -} { - const { transformedPushConnector, references } = transformPushConnectorIdToReference( - PushConnectorIdReferenceName[fieldType], - externalService - ); - - return { - transformedActionDetails: transformedPushConnector.external_service, - references, - }; -} - -type PushConnectorIdRefNameType = - | typeof PUSH_CONNECTOR_ID_REFERENCE_NAME - | typeof USER_ACTION_OLD_PUSH_ID_REF_NAME; - -export const transformPushConnectorIdToReference = ( - referenceName: PushConnectorIdRefNameType, - external_service?: { connector_id?: string | null } | null -): { - transformedPushConnector: { external_service: {} | null }; - references: SavedObjectReference[]; -} => { - const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; - - const references = createConnectorReference( - pushConnectorId, - ACTION_SAVED_OBJECT_TYPE, - referenceName - ); - - return { - transformedPushConnector: { external_service: external_service ? restExternalService : null }, - references, - }; -}; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts deleted file mode 100644 index 3c67535255ec..000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/types.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. - */ - -/** - * Indicates whether which user action field is being parsed, the new_value or the old_value. - */ -export enum UserActionFieldType { - New = 'New', - Old = 'Old', -} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 68f0ba43d889..964e9135aba7 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should create a user action when deleting a case', async () => { + it('should create a user action when creating a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); await deleteCases({ supertest, caseIDs: [postedCase.id] }); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -106,8 +106,6 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, old_value: null, new_value: null, - new_val_connector_id: null, - old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index d7c506a6b69d..63b2f2e9b90e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -126,8 +126,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'update', action_by: defaultUser, new_value: CaseStatuses.closed, - new_val_connector_id: null, - old_val_connector_id: null, old_value: CaseStatuses.open, case_id: `${postedCase.id}`, comment_id: null, @@ -167,8 +165,6 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, new_value: CaseStatuses['in-progress'], old_value: CaseStatuses.open, - old_val_connector_id: null, - new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 13408c5d309d..96709ee7c309 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -114,8 +114,6 @@ export default ({ getService }: FtrProviderContext): void => { const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; const parsedNewValue = JSON.parse(new_value!); - const { id: connectorId, ...restCaseConnector } = postedCase.connector; - expect(rest).to.eql({ action_field: [ 'description', @@ -129,9 +127,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, old_value: null, - old_val_connector_id: null, - // the connector id will be null here because it the connector is none - new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -143,7 +138,7 @@ export default ({ getService }: FtrProviderContext): void => { description: postedCase.description, title: postedCase.title, tags: postedCase.tags, - connector: restCaseConnector, + connector: postedCase.connector, settings: postedCase.settings, owner: postedCase.owner, }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index 942293437b03..f4c31c052cdd 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -148,9 +148,7 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}","owner":"securitySolutionFixture"}`, - new_val_connector_id: null, old_value: null, - old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: `${patchedCase.comments![0].id}`, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 4cae10510d28..35ebb1a4bf7b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -48,15 +48,6 @@ export default ({ getService }: FtrProviderContext): void => { }); it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings, owner]`, async () => { - const { id: connectorId, ...restConnector } = userActionPostResp.connector; - - const userActionNewValueNoId = { - ...userActionPostResp, - connector: { - ...restConnector, - }, - }; - const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -82,10 +73,7 @@ export default ({ getService }: FtrProviderContext): void => { ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); - expect(body[0].old_val_connector_id).to.eql(null); - // this will be null because it is for the none connector - expect(body[0].new_val_connector_id).to.eql(null); - expect(JSON.parse(body[0].new_value)).to.eql(userActionNewValueNoId); + expect(JSON.parse(body[0].new_value)).to.eql(userActionPostResp); }); it(`on close case, user action: 'update' should be called with actionFields: ['status']`, async () => { @@ -159,19 +147,18 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(2); expect(body[1].action_field).to.eql(['connector']); expect(body[1].action).to.eql('update'); - // this is null because it is the none connector - expect(body[1].old_val_connector_id).to.eql(null); expect(JSON.parse(body[1].old_value)).to.eql({ + id: 'none', name: 'none', type: '.none', fields: null, }); expect(JSON.parse(body[1].new_value)).to.eql({ + id: '123', name: 'Connector', type: '.jira', fields: { issueType: 'Task', priority: 'High', parent: null }, }); - expect(body[1].new_val_connector_id).to.eql('123'); }); it(`on update tags, user action: 'add' and 'delete' should be called with actionFields: ['tags']`, async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index f9e66880c523..b4c2dca47bf5 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -12,10 +12,6 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../../../../plugins/cases/common/constants'; import { getCaseUserActions } from '../../../../common/lib/utils'; -import { - CaseUserActionResponse, - CaseUserActionsResponse, -} from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { @@ -45,18 +41,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorUserAction.action_field.length).eql(1); expect(connectorUserAction.action_field[0]).eql('connector'); - expect(connectorUserAction.old_val_connector_id).to.eql( - 'c1900ac0-017f-11eb-93f8-d161651bf509' - ); expect(oldValue).to.eql({ + id: 'c1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, }); - expect(connectorUserAction.new_val_connector_id).to.eql( - 'b1900ac0-017f-11eb-93f8-d161651bf509' - ); expect(newValue).to.eql({ + id: 'b1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, @@ -85,142 +77,5 @@ export default function createGetTests({ getService }: FtrProviderContext) { } }); }); - - describe('7.13 connector id extraction', () => { - let userActions: CaseUserActionsResponse; - - before(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' - ); - }); - - after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' - ); - }); - - describe('none connector case', () => { - it('removes the connector id from the case create user action and sets the ids to null', async () => { - userActions = await getCaseUserActions({ - supertest, - caseID: 'aa8ac630-005e-11ec-91f1-6daf2ab59fb5', - }); - - const userAction = getUserActionById( - userActions, - 'ab43b5f0-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.description).to.be('a description'); - expect(newValDecoded.title).to.be('a case'); - expect(newValDecoded.connector).not.have.property('id'); - // the connector id should be none so it should be removed - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('sets the connector ids to null for a create user action with null new and old values', async () => { - const userAction = getUserActionById( - userActions, - 'b3094de0-005e-11ec-91f1-6daf2ab59fb5' - )!; - - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - }); - - describe('case with many user actions', () => { - before(async () => { - userActions = await getCaseUserActions({ - supertest, - caseID: 'e6fa9370-005e-11ec-91f1-6daf2ab59fb5', - }); - }); - - it('removes the connector id field for a created case user action', async () => { - const userAction = getUserActionById( - userActions, - 'e7882d70-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.description).to.be('a description'); - expect(newValDecoded.title).to.be('a case'); - - expect(newValDecoded.connector).to.not.have.property('id'); - expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('removes the connector id from the external service new value', async () => { - const userAction = getUserActionById( - userActions, - 'e9471b80-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.connector_name).to.be('a jira connector'); - expect(newValDecoded).to.not.have.property('connector_id'); - expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('sets the connector ids to null for a comment user action', async () => { - const userAction = getUserActionById( - userActions, - 'efe9de50-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.comment).to.be('a comment'); - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('removes the connector id for an update connector action', async () => { - const userAction = getUserActionById( - userActions, - '16cd9e30-005f-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - const oldValDecoded = JSON.parse(userAction.old_value!); - - expect(newValDecoded.name).to.be('a different jira connector'); - expect(oldValDecoded.name).to.be('a jira connector'); - - expect(newValDecoded).to.not.have.property('id'); - expect(oldValDecoded).to.not.have.property('id'); - expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - }); - - it('removes the connector id from the external service new value for second push', async () => { - const userAction = getUserActionById( - userActions, - '1ea33bb0-005f-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - - expect(newValDecoded.connector_name).to.be('a different jira connector'); - - expect(newValDecoded).to.not.have.property('connector_id'); - expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - }); - }); }); } - -function getUserActionById( - userActions: CaseUserActionsResponse, - id: string -): CaseUserActionResponse | undefined { - return userActions.find((userAction) => userAction.action_id === id); -} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 0ea66d35b63b..94fe494fc7cc 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -275,8 +275,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'push-to-service', action_by: defaultUser, old_value: null, - old_val_connector_id: null, - new_val_connector_id: connector.id, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -286,6 +284,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(parsedNewValue).to.eql({ pushed_at: pushedCase.external_service!.pushed_at, pushed_by: defaultUser, + connector_id: connector.id, connector_name: connector.name, external_id: '123', external_title: 'INC01', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 255a2a4ce28b..79af6bb279a3 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -108,10 +108,8 @@ export default ({ getService }: FtrProviderContext): void => { expect(body[1].action_field).to.eql(['pushed']); expect(body[1].action).to.eql('push-to-service'); expect(body[1].old_value).to.eql(null); - expect(body[1].old_val_connector_id).to.eql(null); - expect(body[1].new_val_connector_id).to.eql(configure.connector.id); const newValue = JSON.parse(body[1].new_value); - expect(newValue).to.not.have.property('connector_id'); + expect(newValue.connector_id).to.eql(configure.connector.id); expect(newValue.pushed_by).to.eql(defaultUser); }); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz deleted file mode 100644 index 5f73dfd89d1660ef207e6ec26ce7198f2a722616..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2078 zcmV+(2;ui1iwFpikRD+G17u-zVJ>QOZ*BnXTuXDKI1s+)S5TY-LE>SvxgZy+IT5F>ahZaIhWE-j?DT+?5t5lioJNZyKPi$UR$bs&%<`)2h3okV(a3a zL#Oy&DHWJDNxNT|jw-rg7~^}oGPm^>`SOTBQ4}aL)$g~xMn9kcdl7$V`cR|B4O~BP zqt_0h|8$>z;4rF*UHSD`?VD}BV?wB@n?Bo~_4Cj^nGOj*-2e&YwlJ-7)stzXQl$6l zh6Wf#xgLm6fk0B05?J0t#ZnXtXtJuQ4`NG?L_HWCza&<@-~_JN!GM!|m2&)MuBmhx9q}HCvC}gmQ#_ z+fhiAP6!kL7MvE58|{)2@BjUW$f9tX#Q|M!@kbOLLhJ4>F>@&*`saF2GK$DLhc5DD z4@Y;Kz<&wxUF&#wWg|CggBy49oDxweD&-< zb~uP=O%%kqy?2D7n6?o817_4HLQi#A6T|p`gHoi&YV&;Kh!&9DU`EQUkpQ>*>^d>p zRcqse!#BjXO(=RyYKS+rwIfCl;J@9c08#t<4+mjpmkSxFvQ~^M?wf6@-Y9cWv>TKa zu`x}6x0rysjjNkcEPS!H={QAkk41(0LpobFS25I@I_w+z$nQ5dZ){!e%NyRD_2&{@ zND0~Wv59lv5O~qI{Q!pV-rF3!&^BRRE7IabxCFx4Jx^+cnue!l zJ|JK(w>}RR!qB%}icb;?Te-?HnAYk5A6wi@%Om%3Gnt)hGE~fmCjQn@F_nTKW4MH^O>YEbb(vmNHhKZMDpBhSK34k%}} z16_HGrVj!F1sK!E{n*qNV6(h{xStZ5QshfHTiojakdfp|8LD-HbS4LYl*jGQQxsHj z!VB=0BB2Tjia4bPa*30UROFOT$R9W+8NjqyjU)w({mj!hArW} zQJfE0UYX=hCb>5jcO!7gI@3y)b*7W$sRJ22D+)~AP4dz|^A+@W_Od={mc5oI0DI-E zzZ?qdvI>F<+l@p02 zaX5v>QMt(~d?G+WQ&tye2or%}J0?!wI&9AhQ0W5o2Pj~v#CY_Xo~g@Vt~W$w5tM@T z1zZ}PJ64aN9Cw?bKr~e>^_Txb<0w8>igODIIjNCXWIszp2WS?80jGJr$O0xts=C=+ z0AP-0BLRIkyO?XL)=AmOVkxF$BYWd_D;wE~z}Nxr4lAb1^Z{+a&N!<1|<$U5Iw zRxBaS99|>5;$A8+ zg$Yj)35^VZxS__-2xA(;gQaD+1~znb0krJUJOG&Z8TzH+JZagnc>ptcZO)~I=bF+m z$x-a9QQ19?V Date: Mon, 20 Sep 2021 23:55:06 +0100 Subject: [PATCH 10/36] [Archive Migration] xpack..reporting-api-integ/reporting-security (#112432) * [Archive Migration] xpack..reporting-api-integ/reporting-security Found another spot where the es archive for discover is still in use. Use the kbn archive instead. * Switch to unload, per cr. * unload logstash Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../reporting_and_security/bwc_existing_indexes.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts b/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts index 0da51901f908..168390bc6fc2 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts @@ -19,6 +19,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const reportingAPI = getService('reportingAPI'); + const kibanaServer = getService('kibanaServer'); describe('BWC report generation into existing indexes', () => { let cleanupIndexAlias: () => Promise; @@ -28,7 +29,8 @@ export default function ({ getService }: FtrProviderContext) { await reportingAPI.deleteAllReports(); // data to report on await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/discover'); // includes index pattern for logstash_functional + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + // archive with reporting index mappings v6.2 await esArchiver.load('x-pack/test/functional/es_archives/reporting/bwc/6_2'); @@ -41,8 +43,8 @@ export default function ({ getService }: FtrProviderContext) { }); after('remove index alias', async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/discover'); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await cleanupIndexAlias(); await esArchiver.unload('x-pack/test/functional/es_archives/reporting/bwc/6_2'); From 6980064e5e52b2333de05c55081ff8bf85e22b65 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 20 Sep 2021 20:35:59 -0700 Subject: [PATCH 11/36] [Reporting] Address Canvas PDF test failing in Cloud (#112623) --- x-pack/test/functional/apps/canvas/reports.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/canvas/reports.ts b/x-pack/test/functional/apps/canvas/reports.ts index 468430c39003..e21ec5b404d1 100644 --- a/x-pack/test/functional/apps/canvas/reports.ts +++ b/x-pack/test/functional/apps/canvas/reports.ts @@ -198,7 +198,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { " `); - expect(res.get('content-length')).to.be('20725'); + const contentLength = parseInt(res.get('content-length'), 10); + expect(contentLength >= 20725 && contentLength <= 20726).to.be(true); // contentLength can be between 20725 and 20726 }); it('downloaded PDF base64 string is correct without borders and logo', async function () { From 0af821aaf9b2c2371369b0b455852364e0841c24 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 21 Sep 2021 10:16:29 +0200 Subject: [PATCH 12/36] [kibana_react] Clean up `any` in public non-test files (#111261) * first pass at any cleanup * fix types on TableListView * change generic constraint * fix lint * fix TS in no data card Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/listing/dashboard_listing.tsx | 4 +- src/plugins/kibana_react/README.md | 14 -- .../code_editor/code_editor.test.helpers.tsx | 11 +- .../public/code_editor/code_editor.tsx | 4 +- .../kibana_react/public/context/context.tsx | 8 +- .../kibana_react/public/context/index.ts | 1 - .../no_data_card/elastic_agent_card.tsx | 4 +- .../no_data_card/elastic_beats_card.tsx | 4 +- .../no_data_card/no_data_card.tsx | 12 +- .../react_router_navigate.tsx | 11 +- .../containers/panel_container.tsx | 2 +- .../public/split_panel/context.tsx | 2 +- .../table_list_view/table_list_view.tsx | 37 +++--- .../public/util/mount_point_portal.tsx | 4 +- .../test_helpers/react_mount_serializer.ts | 7 +- .../application/utils/get_table_columns.tsx | 121 ++++++++++-------- 16 files changed, 129 insertions(+), 117 deletions(-) diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index 6508bd9f355e..827e5abf2bd6 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -7,7 +7,7 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiLink, EuiButton, EuiEmptyPrompt, EuiBasicTableColumn } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { attemptLoadDashboardByTitle } from '../lib'; import { DashboardAppServices, DashboardRedirect } from '../../types'; @@ -231,7 +231,7 @@ const getTableColumns = ( sortable: true, }, ...(savedObjectsTagging ? [savedObjectsTagging.ui.getTableColumnDefinition()] : []), - ]; + ] as unknown as Array>>; }; const getNoItemsMessage = ( diff --git a/src/plugins/kibana_react/README.md b/src/plugins/kibana_react/README.md index adbdb628ea9d..5071c6c93f15 100644 --- a/src/plugins/kibana_react/README.md +++ b/src/plugins/kibana_react/README.md @@ -75,20 +75,6 @@ const Demo = ({ kibana }) => { export default withKibana(Demo); ``` -Using `` render prop. - -```tsx -import { UseKibana } from 'kibana-react'; - -const Demo = () => { - return ( - - {(kibana) =>
{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}
} -
- ); -}; -``` - ## `uiSettings` service Wrappers around Core's `uiSettings` service. diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx index cee1ecad2196..e0b868f3a8b0 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx @@ -9,9 +9,9 @@ import React, { useEffect, KeyboardEventHandler } from 'react'; import { monaco } from '@kbn/monaco'; function createEditorInstance() { - const keyDownListeners: any[] = []; - const didShowListeners: any[] = []; - const didHideListeners: any[] = []; + const keyDownListeners: Array<(e?: unknown) => void> = []; + const didShowListeners: Array<(e?: unknown) => void> = []; + const didHideListeners: Array<(e?: unknown) => void> = []; let areSuggestionsVisible = false; const editorInstance = { @@ -69,7 +69,10 @@ type MockedEditor = ReturnType; export const mockedEditorInstance: MockedEditor = createEditorInstance(); // mock -const mockMonacoEditor = ({ editorWillMount, editorDidMount }: any) => { +const mockMonacoEditor = ({ + editorWillMount, + editorDidMount, +}: Record void>) => { editorWillMount(monaco); useEffect(() => { diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 29b905c511d4..07bda19bf6d1 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -135,7 +135,9 @@ export const CodeEditor: React.FC = ({ const MonacoEditor: typeof ReactMonacoEditor = useMemo(() => { const isMockedComponent = typeof ReactMonacoEditor === 'function' && ReactMonacoEditor.name === 'JestMockEditor'; - return isMockedComponent ? (ReactMonacoEditor as any)() : ReactMonacoEditor; + return isMockedComponent + ? (ReactMonacoEditor as unknown as () => typeof ReactMonacoEditor)() + : ReactMonacoEditor; }, []); const isReadOnly = options?.readOnly ?? false; diff --git a/src/plugins/kibana_react/public/context/context.tsx b/src/plugins/kibana_react/public/context/context.tsx index 974bd4e89479..c9dc3df90864 100644 --- a/src/plugins/kibana_react/public/context/context.tsx +++ b/src/plugins/kibana_react/public/context/context.tsx @@ -26,7 +26,7 @@ export const useKibana = (): KibanaReactContextValue< > => useContext(context as unknown as React.Context>); -export const withKibana = }>( +export const withKibana = }>( type: React.ComponentType ): React.FC> => { const EnhancedType: React.FC> = (props: Omit) => { @@ -36,10 +36,6 @@ export const withKibana = return EnhancedType; }; -export const UseKibana: React.FC<{ - children: (kibana: KibanaReactContextValue) => React.ReactNode; -}> = ({ children }) => <>{children(useKibana())}; - export const createKibanaReactContext = ( services: Services ): KibanaReactContext => { @@ -58,7 +54,7 @@ export const createKibanaReactContext = ( () => createKibanaReactContext({ ...services, ...oldValue.services, ...newServices }), [services, oldValue, newServices] ); - return createElement(context.Provider as React.ComponentType, { + return createElement(context.Provider, { value: newValue, children, }); diff --git a/src/plugins/kibana_react/public/context/index.ts b/src/plugins/kibana_react/public/context/index.ts index 12f8bfccbb59..b34951b29883 100644 --- a/src/plugins/kibana_react/public/context/index.ts +++ b/src/plugins/kibana_react/public/context/index.ts @@ -12,6 +12,5 @@ export { KibanaContextProvider, useKibana, withKibana, - UseKibana, } from './context'; export { KibanaReactContext, KibanaReactContextValue, KibanaServices } from './types'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index ad3fb0e2e639..f071bd9fab25 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -26,6 +26,7 @@ export const ElasticAgentCard: FunctionComponent = ({ title, href, button, + layout, ...cardRest }) => { const { @@ -58,7 +59,8 @@ export const ElasticAgentCard: FunctionComponent = ({ image={addBasePath(`${basePathUrl}elastic_agent_card.svg`)} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} footer={footer} - {...(cardRest as any)} + layout={layout as 'vertical' | undefined} + {...cardRest} /> ); }; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx index d0a8ceddb8dc..0372d1209648 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx @@ -23,6 +23,7 @@ export const ElasticBeatsCard: FunctionComponent = ({ button, href, solution, // unused for now + layout, ...cardRest }) => { const { @@ -58,7 +59,8 @@ export const ElasticBeatsCard: FunctionComponent = ({ )} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} footer={footer} - {...(cardRest as any)} + layout={layout as 'vertical' | undefined} + {...cardRest} /> ); }; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx index 682d0267e1a7..9cc38cc5f603 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiButton, EuiCard, EuiCardProps } from '@elastic/eui'; import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page'; @@ -17,6 +18,7 @@ export const NoDataCard: FunctionComponent = ({ recommended, title, button, + layout, ...cardRest }) => { const footer = @@ -25,10 +27,16 @@ export const NoDataCard: FunctionComponent = ({ return ( ); }; diff --git a/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx b/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx index db122d034371..5abd77c9dc9b 100644 --- a/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx +++ b/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx @@ -7,6 +7,7 @@ */ import { ScopedHistory } from 'kibana/public'; +import { MouseEvent } from 'react'; import { History, parsePath } from 'history'; interface LocationObject { @@ -15,10 +16,10 @@ interface LocationObject { hash?: string; } -const isModifiedEvent = (event: any) => +const isModifiedEvent = (event: MouseEvent) => !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); -const isLeftClickEvent = (event: any) => event.button === 0; +const isLeftClickEvent = (event: MouseEvent) => event.button === 0; export const toLocationObject = (to: string | LocationObject) => typeof to === 'string' ? parsePath(to) : to; @@ -34,7 +35,7 @@ export const reactRouterNavigate = ( export const reactRouterOnClickHandler = (history: ScopedHistory | History, to: string | LocationObject, onClickCallback?: Function) => - (event: any) => { + (event: MouseEvent) => { if (onClickCallback) { onClickCallback(event); } @@ -43,7 +44,9 @@ export const reactRouterOnClickHandler = return; } - if (event.target.getAttribute('target')) { + if ( + (event.target as unknown as { getAttribute: (a: string) => unknown })?.getAttribute('target') + ) { return; } diff --git a/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx b/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx index 86f73f933188..69beb565ad85 100644 --- a/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx +++ b/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx @@ -17,7 +17,7 @@ export interface Props { children: ReactNode; className?: string; resizerClassName?: string; - onPanelWidthChange?: (arrayOfPanelWidths: number[]) => any; + onPanelWidthChange?: (arrayOfPanelWidths: number[]) => void; } interface State { diff --git a/src/plugins/kibana_react/public/split_panel/context.tsx b/src/plugins/kibana_react/public/split_panel/context.tsx index bad39f8bd5ef..e236b5037e7f 100644 --- a/src/plugins/kibana_react/public/split_panel/context.tsx +++ b/src/plugins/kibana_react/public/split_panel/context.tsx @@ -12,7 +12,7 @@ import { PanelRegistry } from './registry'; const PanelContext = createContext({ registry: new PanelRegistry() }); interface ContextProps { - children: any; + children: JSX.Element; registry: PanelRegistry; } diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 30d09f4bf865..18a88790b425 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -26,17 +26,13 @@ import React from 'react'; import { KibanaPageTemplate } from '../page_template'; import { toMountPoint } from '../util'; -interface Item { - id?: string; -} - -export interface TableListViewProps { +export interface TableListViewProps { createItem?(): void; - deleteItems?(items: object[]): Promise; - editItem?(item: object): void; + deleteItems?(items: V[]): Promise; + editItem?(item: V): void; entityName: string; entityNamePlural: string; - findItems(query: string): Promise<{ total: number; hits: object[] }>; + findItems(query: string): Promise<{ total: number; hits: V[] }>; listingLimit: number; initialFilter: string; initialPageSize: number; @@ -44,7 +40,7 @@ export interface TableListViewProps { * Should be an EuiEmptyPrompt (but TS doesn't support this typing) */ emptyPrompt?: JSX.Element; - tableColumns: Array>; + tableColumns: Array>; tableListTitle: string; toastNotifications: ToastsStart; /** @@ -63,8 +59,8 @@ export interface TableListViewProps { searchFilters?: SearchFilterConfig[]; } -export interface TableListViewState { - items: object[]; +export interface TableListViewState { + items: V[]; hasInitialFetchReturned: boolean; isFetchingItems: boolean; isDeletingItems: boolean; @@ -81,11 +77,14 @@ export interface TableListViewState { // and not supporting server-side paging. // This component does not try to tackle these problems (yet) and is just feature matching the legacy component // TODO support server side sorting/paging once title and description are sortable on the server. -class TableListView extends React.Component { +class TableListView extends React.Component< + TableListViewProps, + TableListViewState +> { private pagination = {}; private _isMounted = false; - constructor(props: TableListViewProps) { + constructor(props: TableListViewProps) { super(props); this.pagination = { @@ -134,7 +133,7 @@ class TableListView extends React.Component(response.hits, 'title') : response.hits, totalItems: response.total, showLimitError: response.total > this.props.listingLimit, }); @@ -404,17 +403,17 @@ class TableListView extends React.Component { + onSelectionChange: (obj: V[]) => { this.setState({ selectedIds: obj - .map((item) => item.id) - .filter((id: undefined | string): id is string => Boolean(id)), + .map((item) => (item as Record)?.id) + .filter((id): id is string => Boolean(id)), }); }, } : undefined; - const actions: EuiTableActionsColumnType['actions'] = [ + const actions: EuiTableActionsColumnType['actions'] = [ { name: i18n.translate('kibana-react.tableListView.listing.table.editActionName', { defaultMessage: 'Edit', @@ -427,7 +426,7 @@ class TableListView extends React.Component !error, + enabled: (v) => !(v as unknown as { error: string })?.error, onClick: this.props.editItem, }, ]; diff --git a/src/plugins/kibana_react/public/util/mount_point_portal.tsx b/src/plugins/kibana_react/public/util/mount_point_portal.tsx index 07d2edfa717b..d0753c68d1f2 100644 --- a/src/plugins/kibana_react/public/util/mount_point_portal.tsx +++ b/src/plugins/kibana_react/public/util/mount_point_portal.tsx @@ -60,12 +60,12 @@ export const MountPointPortal: React.FC = ({ children, se } }; -class MountPointPortalErrorBoundary extends Component<{}, { error?: any }> { +class MountPointPortalErrorBoundary extends Component<{}, { error?: unknown }> { state = { error: undefined, }; - static getDerivedStateFromError(error: any) { + static getDerivedStateFromError(error: unknown) { return { error }; } diff --git a/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts index 6438ff0b8d98..4d1575bf1618 100644 --- a/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts +++ b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ -export function test(value: any) { +export function test(value?: Record) { return value && value.__reactMount__; } -export function print(value: any, serialize: any) { +export function print( + value: Record, + serialize: (args: Record) => { replace: (s1: string, s2: string) => unknown } +) { // there is no proper way to correctly indent multiline values // so the trick here is to use the Object representation and rewriting the root object name return serialize({ diff --git a/src/plugins/visualize/public/application/utils/get_table_columns.tsx b/src/plugins/visualize/public/application/utils/get_table_columns.tsx index 8c8ecaf9a448..69383005deb0 100644 --- a/src/plugins/visualize/public/application/utils/get_table_columns.tsx +++ b/src/plugins/visualize/public/application/utils/get_table_columns.tsx @@ -8,7 +8,15 @@ import React from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; -import { EuiBetaBadge, EuiButton, EuiEmptyPrompt, EuiIcon, EuiLink, EuiBadge } from '@elastic/eui'; +import { + EuiBetaBadge, + EuiButton, + EuiEmptyPrompt, + EuiIcon, + EuiLink, + EuiBadge, + EuiBasicTableColumn, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; @@ -86,61 +94,62 @@ export const getTableColumns = ( application: ApplicationStart, kbnUrlStateStorage: IKbnUrlStateStorage, taggingApi?: SavedObjectsTaggingApi -) => [ - { - field: 'title', - name: i18n.translate('visualize.listing.table.titleColumnName', { - defaultMessage: 'Title', - }), - sortable: true, - render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) => - // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link - !error ? ( - - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - { - doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name); - }} - data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`} - > - {field} - - - ) : ( - field - ), - }, - { - field: 'typeTitle', - name: i18n.translate('visualize.listing.table.typeColumnName', { - defaultMessage: 'Type', - }), - sortable: true, - render: (field: string, record: VisualizationListItem) => - !record.error ? ( - - {renderItemTypeIcon(record)} - {record.typeTitle} - {getBadge(record)} - - ) : ( - - {record.error} - - ), - }, - { - field: 'description', - name: i18n.translate('visualize.listing.table.descriptionColumnName', { - defaultMessage: 'Description', - }), - sortable: true, - render: (field: string, record: VisualizationListItem) => {record.description}, - }, - ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []), -]; +) => + [ + { + field: 'title', + name: i18n.translate('visualize.listing.table.titleColumnName', { + defaultMessage: 'Title', + }), + sortable: true, + render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) => + // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link + !error ? ( + + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { + doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name); + }} + data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`} + > + {field} + + + ) : ( + field + ), + }, + { + field: 'typeTitle', + name: i18n.translate('visualize.listing.table.typeColumnName', { + defaultMessage: 'Type', + }), + sortable: true, + render: (field: string, record: VisualizationListItem) => + !record.error ? ( + + {renderItemTypeIcon(record)} + {record.typeTitle} + {getBadge(record)} + + ) : ( + + {record.error} + + ), + }, + { + field: 'description', + name: i18n.translate('visualize.listing.table.descriptionColumnName', { + defaultMessage: 'Description', + }), + sortable: true, + render: (field: string, record: VisualizationListItem) => {record.description}, + }, + ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []), + ] as unknown as Array>>; export const getNoItemsMessage = (createItem: () => void) => ( Date: Tue, 21 Sep 2021 10:30:30 +0200 Subject: [PATCH 13/36] [SOM] Add `visibleInManagement` option to management metadata (#112073) * implement SavedObjectsTypeManagementDefinition.visibleInManagement * update generated doc * improve FTR tests * fix FTR test --- ...in-core-server.deprecationsservicesetup.md | 1 + ...er.savedobjectstypemanagementdefinition.md | 1 + ...anagementdefinition.visibleinmanagement.md | 18 + .../saved_objects_type_registry.test.ts | 53 ++ .../saved_objects_type_registry.ts | 5 + src/core/server/saved_objects/types.ts | 9 + src/core/server/server.api.md | 1 + .../server/routes/get_allowed_types.ts | 1 + .../visible_in_management/data.json | 20 + .../visible_in_management/mappings.json | 477 ++++++++++++++++++ .../server/plugin.ts | 36 ++ .../_import_non_visible_in_management.ndjson | 1 + .../saved_objects_management/index.ts | 1 + .../visible_in_management.ts | 113 +++++ 14 files changed, 737 insertions(+) create mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md create mode 100644 test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json create mode 100644 test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json create mode 100644 test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson create mode 100644 test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md index 7b2cbdecd146..2bc7f6cba594 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md @@ -27,6 +27,7 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations const deprecations: DeprecationsDetails[] = []; const count = await getFooCount(savedObjectsClient); if (count > 0) { + // Example of a manual correctiveAction deprecations.push({ title: i18n.translate('xpack.foo.deprecations.title', { defaultMessage: `Foo's are deprecated` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md index 8c42884eb0b3..2c3dd9f3f843 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md @@ -25,4 +25,5 @@ export interface SavedObjectsTypeManagementDefinition | [isExportable](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.isexportable.md) | SavedObjectsExportablePredicate<Attributes> | Optional hook to specify whether an object should be exportable.If specified, isExportable will be called during export for each of this type's objects in the export, and the ones not matching the predicate will be excluded from the export.When implementing both isExportable and onExport, it is mandatory that isExportable returns the same value for an object before and after going though the export transform. E.g isExportable(objectBeforeTransform) === isExportable(objectAfterTransform) | | [onExport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md) | SavedObjectsExportTransform<Attributes> | An optional export transform function that can be used transform the objects of the registered type during the export process.It can be used to either mutate the exported objects, or add additional objects (of any type) to the export list.See [the transform type documentation](./kibana-plugin-core-server.savedobjectsexporttransform.md) for more info and examples.When implementing both isExportable and onExport, it is mandatory that isExportable returns the same value for an object before and after going though the export transform. E.g isExportable(objectBeforeTransform) === isExportable(objectAfterTransform) | | [onImport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md) | SavedObjectsImportHook<Attributes> | An optional [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) to use when importing given type.Import hooks are executed during the savedObjects import process and allow to interact with the imported objects. See the [hook documentation](./kibana-plugin-core-server.savedobjectsimporthook.md) for more info. | +| [visibleInManagement](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md) | boolean | When set to false, the type will not be listed or searchable in the SO management section. Main usage of setting this property to false for a type is when objects from the type should be included in the export via references or export hooks, but should not directly appear in the SOM. Defaults to true. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md new file mode 100644 index 000000000000..33ddc8e8c830 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) > [visibleInManagement](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md) + +## SavedObjectsTypeManagementDefinition.visibleInManagement property + +When set to false, the type will not be listed or searchable in the SO management section. Main usage of setting this property to false for a type is when objects from the type should be included in the export via references or export hooks, but should not directly appear in the SOM. Defaults to `true`. + +Signature: + +```typescript +visibleInManagement?: boolean; +``` + +## Remarks + +`importableAndExportable` must be `true` to specify this property. + diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/src/core/server/saved_objects/saved_objects_type_registry.test.ts index 872b61706c52..c86a1a54f14a 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.test.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.test.ts @@ -47,6 +47,59 @@ describe('SavedObjectTypeRegistry', () => { }).toThrowErrorMatchingInlineSnapshot(`"Type 'typeA' is already registered"`); }); + it('throws when `management.visibleInManagement` is specified but `management.importableAndExportable` is undefined or false', () => { + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + visibleInManagement: true, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + visibleInManagement: false, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: false, + visibleInManagement: false, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: true, + visibleInManagement: false, + }, + }) + ); + }).not.toThrow(); + }); + it('throws when `management.onExport` is specified but `management.importableAndExportable` is undefined or false', () => { expect(() => { registry.registerType( diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/src/core/server/saved_objects/saved_objects_type_registry.ts index ba5960c59239..444af58eee80 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.ts @@ -134,5 +134,10 @@ const validateType = ({ name, management }: SavedObjectsType) => { `Type ${name}: 'management.importableAndExportable' must be 'true' when specifying 'management.onExport'` ); } + if (management.visibleInManagement !== undefined && !management.importableAndExportable) { + throw new Error( + `Type ${name}: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'` + ); + } } }; diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 83f46c521a83..b2a6be38bb52 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -356,6 +356,15 @@ export interface SavedObjectsTypeManagementDefinition { * Is the type importable or exportable. Defaults to `false`. */ importableAndExportable?: boolean; + /** + * When set to false, the type will not be listed or searchable in the SO management section. + * Main usage of setting this property to false for a type is when objects from the type should + * be included in the export via references or export hooks, but should not directly appear in the SOM. + * Defaults to `true`. + * + * @remarks `importableAndExportable` must be `true` to specify this property. + */ + visibleInManagement?: boolean; /** * The default search field to use for this type. Defaults to `id`. */ diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ba7973fa1ce0..3c7aa8b99268 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2751,6 +2751,7 @@ export interface SavedObjectsTypeManagementDefinition { isExportable?: SavedObjectsExportablePredicate; onExport?: SavedObjectsExportTransform; onImport?: SavedObjectsImportHook; + visibleInManagement?: boolean; } // @public diff --git a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts index 13adf2764b55..ce371ea59023 100644 --- a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts +++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts @@ -17,6 +17,7 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => { async (context, req, res) => { const allowedTypes = context.core.savedObjects.typeRegistry .getImportableAndExportableTypes() + .filter((type) => type.management!.visibleInManagement ?? true) .map((type) => type.name); return res.ok({ diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json new file mode 100644 index 000000000000..236d7bbff721 --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json @@ -0,0 +1,20 @@ +{ + "type": "doc", + "value": { + "id": "test-not-visible-in-management:vim-1", + "index": ".kibana", + "source": { + "coreMigrationVersion": "7.14.0", + "references": [ + ], + "test-not-visible-in-management": { + "enabled": true, + "title": "vim-1" + }, + "type": "test-not-visible-in-management", + "updated_at": "2018-12-21T00:43:07.096Z" + }, + "type": "_doc" + } +} + diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json new file mode 100644 index 000000000000..3c0a975a30be --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json @@ -0,0 +1,477 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana_$KIBANA_PACKAGE_VERSION": {}, + ".kibana": {} + }, + "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", + "dashboard": "40554caf09725935e2c02e02563a2d07", + "index-pattern": "45915a1ad866812242df474eb0479052", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "legacy-url-alias": "6155300fd11a00e23d5cbaa39f0fce0a", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "db2c00e39b36f40930a3b9fc71c823e1", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355" + } + }, + "dynamic": "strict", + "properties": { + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "type": "object" + }, + "canvas-workpad": { + "dynamic": "false", + "type": "object" + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "coreMigrationVersion": { + "type": "keyword" + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "graph-workspace": { + "dynamic": "false", + "type": "object" + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "legacy-url-alias": { + "dynamic": "false", + "properties": { + "disabled": { + "type": "boolean" + }, + "sourceId": { + "type": "keyword" + }, + "targetType": { + "type": "keyword" + } + } + }, + "map": { + "dynamic": "false", + "type": "object" + }, + "migrationVersion": { + "dynamic": "true", + "type": "object" + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "grid": { + "enabled": false, + "type": "object" + }, + "hideChart": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "server": { + "dynamic": "false", + "type": "object" + }, + "space": { + "dynamic": "false", + "type": "object" + }, + "spaceId": { + "type": "keyword" + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "test-export-add": { + "dynamic": "false", + "type": "object" + }, + "test-export-add-dep": { + "dynamic": "false", + "type": "object" + }, + "test-export-invalid-transform": { + "dynamic": "false", + "type": "object" + }, + "test-export-transform": { + "dynamic": "false", + "type": "object" + }, + "test-export-transform-error": { + "dynamic": "false", + "type": "object" + }, + "test-visible-in-management": { + "dynamic": "false", + "type": "object" + }, + "test-not-visible-in-management": { + "dynamic": "false", + "type": "object" + }, + "type": { + "type": "keyword" + }, + "ui-counter": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "usage-counters": { + "dynamic": "false", + "properties": { + "domainId": { + "type": "keyword" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1", + "priority": "10", + "refresh_interval": "1s", + "routing_partition_size": "1" + } + } + } +} diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts index 15afdb229b1f..0cb6a5ba8eb5 100644 --- a/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts +++ b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts @@ -176,6 +176,42 @@ export class SavedObjectExportTransformsPlugin implements Plugin { }, }, }); + + // example of a SO type with `visibleInManagement: false` + savedObjects.registerType<{ enabled: boolean; title: string }>({ + name: 'test-not-visible-in-management', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + enabled: { type: 'boolean' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + visibleInManagement: false, + }, + }); + + // example of a SO type with `visibleInManagement: true` + savedObjects.registerType<{ enabled: boolean; title: string }>({ + name: 'test-visible-in-management', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + enabled: { type: 'boolean' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + visibleInManagement: true, + }, + }); } public start() {} diff --git a/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson new file mode 100644 index 000000000000..754848a99d03 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson @@ -0,0 +1 @@ +{"attributes": { "title": "Saved object type that is not visible in management" }, "id":"ff3773b0-9ate-11e7-ahb3-3dcb94193fab", "references":[], "type":"test-not-visible-in-management", "version":1} diff --git a/test/plugin_functional/test_suites/saved_objects_management/index.ts b/test/plugin_functional/test_suites/saved_objects_management/index.ts index 03ac96b9a11f..b7730a95d3c7 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/index.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/index.ts @@ -16,5 +16,6 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) { loadTestFile(require.resolve('./export_transform')); loadTestFile(require.resolve('./import_warnings')); loadTestFile(require.resolve('./hidden_types')); + loadTestFile(require.resolve('./visible_in_management')); }); } diff --git a/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts new file mode 100644 index 000000000000..dd43ba80a8e8 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { join } from 'path'; +import expect from '@kbn/expect'; +import type { Response } from 'supertest'; +import type { PluginFunctionalProviderContext } from '../../services'; +import { SavedObject } from '../../../../src/core/types'; + +function parseNdJson(input: string): Array> { + return input.split('\n').map((str) => JSON.parse(str)); +} + +export default function ({ getService }: PluginFunctionalProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('types with `visibleInManagement` ', () => { + before(async () => { + await esArchiver.load( + 'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management' + ); + }); + + after(async () => { + await esArchiver.unload( + 'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management' + ); + }); + + describe('export', () => { + it('allows to export them directly by id', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + objects: [ + { + type: 'test-not-visible-in-management', + id: 'vim-1', + }, + ], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['vim-1']); + }); + }); + + it('allows to export them directly by type', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-not-visible-in-management'], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['vim-1']); + }); + }); + }); + + describe('import', () => { + it('allows to import them', async () => { + await supertest + .post('/api/saved_objects/_import') + .set('kbn-xsrf', 'true') + .attach('file', join(__dirname, './exports/_import_non_visible_in_management.ndjson')) + .expect(200) + .then((resp) => { + expect(resp.body).to.eql({ + success: true, + successCount: 1, + successResults: [ + { + id: 'ff3773b0-9ate-11e7-ahb3-3dcb94193fab', + meta: { + title: 'Saved object type that is not visible in management', + }, + type: 'test-not-visible-in-management', + }, + ], + warnings: [], + }); + }); + }); + }); + + describe('savedObjects management APIS', () => { + it('GET /api/kibana/management/saved_objects/_allowed_types should only return types that are `visibleInManagement: true`', async () => + await supertest + .get('/api/kibana/management/saved_objects/_allowed_types') + .set('kbn-xsrf', 'true') + .expect(200) + .then((response: Response) => { + const { types } = response.body; + expect(types.includes('test-is-exportable')).to.eql(true); + expect(types.includes('test-visible-in-management')).to.eql(true); + expect(types.includes('test-not-visible-in-management')).to.eql(false); + })); + }); + }); +} From bb4f1360a8a7b986393e8cf3e7165352707df71e Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 21 Sep 2021 10:34:57 +0200 Subject: [PATCH 14/36] remove last usages of plugin async lifecycles (#112111) * remove last usages of plugin async lifecycles * fix contract type * fix types. again. * remove unused import --- src/plugins/presentation_util/README.mdx | 2 +- x-pack/plugins/lists/server/plugin.ts | 8 +++----- x-pack/plugins/monitoring/server/plugin.ts | 2 +- .../plugins/kibana_cors_test/server/plugin.ts | 14 ++++++-------- .../application_usage_test/public/plugin.ts | 10 +++++++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/plugins/presentation_util/README.mdx b/src/plugins/presentation_util/README.mdx index 35b80e363453..575e8002e6eb 100755 --- a/src/plugins/presentation_util/README.mdx +++ b/src/plugins/presentation_util/README.mdx @@ -162,7 +162,7 @@ Once your services and providers are defined, and you have at least one set of f import { pluginServices } from './services'; import { registry } from './services/kibana'; - public async start( + public start( coreStart: CoreStart, startPlugins: StartDeps ): Promise { diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index 6a1b0733c186..7fa1bc460723 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -27,9 +27,7 @@ import { getUser } from './get_user'; import { initSavedObjects } from './saved_objects'; import { ExceptionListClient } from './services/exception_lists/exception_list_client'; -export class ListPlugin - implements Plugin, ListsPluginStart, {}, PluginsStart> -{ +export class ListPlugin implements Plugin { private readonly logger: Logger; private readonly config: ConfigType; private spaces: SpacesServiceStart | undefined | null; @@ -40,7 +38,7 @@ export class ListPlugin this.config = this.initializerContext.config.get(); } - public async setup(core: CoreSetup): Promise { + public setup(core: CoreSetup): ListPluginSetup { const { config } = this; initSavedObjects(core.savedObjects); @@ -70,7 +68,7 @@ export class ListPlugin }; } - public start(core: CoreStart, plugins: PluginsStart): void { + public start(core: CoreStart, plugins: PluginsStart): ListsPluginStart { this.logger.debug('Starting plugin'); this.security = plugins.security; this.spaces = plugins.spaces?.spacesService; diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 3b939e736165..18eb8fd4d4dd 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -207,7 +207,7 @@ export class MonitoringPlugin } } - async start(coreStart: CoreStart, { licensing }: PluginsStart) { + start(coreStart: CoreStart, { licensing }: PluginsStart) { const config = this.config!; this.cluster = instantiateClient( config.ui.elasticsearch, diff --git a/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts b/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts index e6c3f4b05aab..9ba373c02faf 100644 --- a/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts +++ b/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts @@ -7,7 +7,6 @@ import Hapi from '@hapi/hapi'; import { kbnTestConfig } from '@kbn/test'; -import { take } from 'rxjs/operators'; import Url from 'url'; import abab from 'abab'; @@ -47,20 +46,18 @@ fetch('${url}', { export class CorsTestPlugin implements Plugin { private server?: Hapi.Server; + constructor(private readonly initializerContext: PluginInitializerContext) {} - async setup(core: CoreSetup) { + setup(core: CoreSetup) { const router = core.http.createRouter(); router.post({ path: '/cors-test', validate: false }, (context, req, res) => res.ok({ body: 'content from kibana' }) ); } - async start(core: CoreStart) { - const config = await this.initializerContext.config - .create() - .pipe(take(1)) - .toPromise(); + start(core: CoreStart) { + const config = this.initializerContext.config.get(); const server = new Hapi.Server({ port: config.port, @@ -78,8 +75,9 @@ export class CorsTestPlugin implements Plugin { return h.response(renderBody(kibanaUrl)); }, }); - await server.start(); + server.start(); } + public stop() { if (this.server) { this.server.stop(); diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts index b0c777593b02..ff1e89b58c7e 100644 --- a/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts +++ b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts @@ -12,8 +12,12 @@ import './types'; export class ApplicationUsageTest implements Plugin { public setup(core: CoreSetup) {} - public async start(core: CoreStart) { - const applications = await core.application.applications$.pipe(first()).toPromise(); - window.__applicationIds__ = [...applications.keys()]; + public start(core: CoreStart) { + core.application.applications$ + .pipe(first()) + .toPromise() + .then((applications) => { + window.__applicationIds__ = [...applications.keys()]; + }); } } From 60500c20afbaa1707f52015c4722ae83f4a47473 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 21 Sep 2021 13:45:49 +0300 Subject: [PATCH 15/36] Move timeseries to vis_types folder (#112228) * Move timeseries to vis_types folder * Fix jest * Fix types * Fix more types * Fix types * fix jest tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .eslintrc.js | 2 +- .github/CODEOWNERS | 2 +- .i18nrc.json | 2 +- api_docs/data.json | 320 +++++++++--------- api_docs/data_index_patterns.json | 116 +++---- api_docs/deprecations_by_plugin.mdx | 36 +- api_docs/vis_type_timeseries.json | 20 +- docs/developer/plugin-list.asciidoc | 2 +- src/plugins/vis_type_timeseries/tsconfig.json | 26 -- .../common/__mocks__/index_patterns_utils.ts | 0 .../timeseries}/common/agg_utils.test.ts | 2 +- .../timeseries}/common/agg_utils.ts | 2 +- .../timeseries}/common/basic_aggs.ts | 0 .../common/calculate_label.test.ts | 0 .../timeseries}/common/calculate_label.ts | 2 +- .../timeseries}/common/constants.ts | 0 .../timeseries}/common/empty_label.ts | 0 .../timeseries}/common/enums/index.ts | 0 .../timeseries}/common/enums/metric_types.ts | 0 .../timeseries}/common/enums/model_types.ts | 0 .../timeseries}/common/enums/panel_types.ts | 0 .../common/enums/timerange_data_modes.ts | 0 .../timeseries}/common/errors.ts | 0 .../timeseries}/common/fields_utils.test.ts | 2 +- .../timeseries}/common/fields_utils.ts | 4 +- .../common/index_patterns_utils.test.ts | 2 +- .../common/index_patterns_utils.ts | 2 +- .../common/interval_regexp.test.ts | 0 .../timeseries}/common/interval_regexp.ts | 0 .../common/last_value_utils.test.ts | 0 .../timeseries}/common/last_value_utils.ts | 0 .../common/operators_utils.test.ts | 0 .../timeseries}/common/operators_utils.ts | 0 .../common/to_percentile_number.ts | 0 .../timeseries}/common/types/color_rules.ts | 0 .../timeseries}/common/types/index.ts | 2 +- .../timeseries}/common/types/panel_model.ts | 2 +- .../timeseries}/common/types/vis_data.ts | 0 .../timeseries}/common/ui_restrictions.ts | 0 .../timeseries}/common/validate_interval.ts | 2 +- .../timeseries}/common/vis_data_utils.ts | 0 .../timeseries}/jest.config.js | 8 +- .../timeseries}/kibana.json | 0 .../public/application/_mixins.scss | 0 .../public/application/_tvb_editor.scss | 0 .../public/application/_variables.scss | 0 .../components/_annotations_editor.scss | 0 .../application/components/_color_picker.scss | 0 .../application/components/_color_rules.scss | 0 .../public/application/components/_error.scss | 0 .../public/application/components/_index.scss | 0 .../components/_markdown_editor.scss | 0 .../application/components/_no_data.scss | 0 .../components/_series_editor.scss | 0 .../application/components/_vis_editor.scss | 0 .../components/_vis_editor_visualization.scss | 0 .../application/components/_vis_picker.scss | 0 .../components/_vis_with_splits.scss | 0 .../components/add_delete_buttons.test.tsx | 0 .../components/add_delete_buttons.tsx | 0 .../application/components/aggs/_agg_row.scss | 0 .../application/components/aggs/_index.scss | 0 .../application/components/aggs/agg.tsx | 0 .../application/components/aggs/agg_row.tsx | 0 .../components/aggs/agg_select.test.tsx | 0 .../components/aggs/agg_select.tsx | 0 .../application/components/aggs/aggs.tsx | 0 .../components/aggs/calculation.js | 0 .../components/aggs/cumulative_sum.js | 0 .../application/components/aggs/derivative.js | 0 .../components/aggs/field_select.tsx | 0 .../components/aggs/filter_ratio.js | 2 +- .../components/aggs/filter_ratio.test.js | 2 +- .../components/aggs/histogram_support.test.js | 2 +- .../components/aggs/invalid_agg.tsx | 0 .../application/components/aggs/math.js | 0 .../components/aggs/metric_select.js | 0 .../components/aggs/moving_average.js | 0 .../application/components/aggs/percentile.js | 2 +- .../components/aggs/percentile_hdr.tsx | 0 .../components/aggs/percentile_rank/index.js | 0 .../percentile_rank/multi_value_row.test.tsx | 0 .../aggs/percentile_rank/multi_value_row.tsx | 0 .../aggs/percentile_rank/percentile_rank.tsx | 2 +- .../percentile_rank_values.tsx | 0 .../components/aggs/percentile_ui.js | 0 .../components/aggs/percentile_ui.test.tsx | 0 .../components/aggs/positive_only.js | 0 .../components/aggs/positive_rate.js | 2 +- .../components/aggs/serial_diff.js | 0 .../application/components/aggs/series_agg.js | 0 .../application/components/aggs/static.js | 0 .../application/components/aggs/std_agg.js | 0 .../components/aggs/std_deviation.js | 2 +- .../components/aggs/std_sibling.js | 0 .../application/components/aggs/top_hit.js | 2 +- .../application/components/aggs/vars.js | 0 .../application/components/annotation_row.tsx | 2 +- .../components/annotations_editor.tsx | 0 .../components/color_picker.test.tsx | 0 .../application/components/color_picker.tsx | 0 .../components/color_rules.test.tsx | 0 .../application/components/color_rules.tsx | 0 .../components/data_format_picker.tsx | 0 .../public/application/components/error.js | 0 .../__snapshots__/icon_select.test.js.snap | 0 .../components/icon_select/icon_select.js | 0 .../icon_select/icon_select.test.js | 0 .../application/components/index_pattern.js | 4 +- .../components/last_value_mode_indicator.tsx | 0 .../components/last_value_mode_popover.scss | 0 .../components/last_value_mode_popover.tsx | 0 .../components/lib/agg_to_component.js | 0 .../components/lib/calculate_siblings.js | 0 .../components/lib/calculate_siblings.test.js | 0 .../lib/check_if_numeric_metric.test.ts | 2 +- .../components/lib/check_if_numeric_metric.ts | 2 +- ...eck_if_series_have_same_formatters.test.ts | 0 .../check_if_series_have_same_formatters.ts | 2 +- .../components/lib/collection_actions.test.ts | 0 .../components/lib/collection_actions.ts | 0 .../lib/convert_series_to_datatable.test.ts | 0 .../lib/convert_series_to_datatable.ts | 0 .../components/lib/convert_series_to_vars.js | 0 .../lib/convert_series_to_vars.test.js | 0 .../components/lib/create_change_handler.js | 0 .../lib/create_field_formatter.test.ts | 2 +- .../components/lib/create_field_formatter.ts | 6 +- .../lib/create_interval_based_formatter.ts | 0 .../lib/create_number_handler.test.ts | 0 .../components/lib/create_number_handler.ts | 0 .../lib/create_select_handler.test.ts | 0 .../components/lib/create_select_handler.ts | 0 .../lib/create_text_handler.test.ts | 0 .../components/lib/create_text_handler.ts | 0 .../components/lib/durations.test.ts | 0 .../application/components/lib/durations.ts | 0 .../components/lib/get_axis_label_string.js | 0 .../lib/get_axis_label_string.test.js | 0 .../lib/get_click_filter_data.test.ts | 0 .../components/lib/get_click_filter_data.ts | 0 .../lib/get_default_query_language.ts | 2 +- .../components/lib/get_display_name.js | 0 .../components/lib/get_formatter_type.test.ts | 0 .../components/lib/get_formatter_type.ts | 0 .../components/lib/get_interval.ts | 2 +- .../components/lib/get_metrics_field.test.ts | 0 .../components/lib/get_metrics_field.ts | 0 .../get_supported_fields_by_metric_type.js | 4 +- ...et_supported_fields_by_metric_type.test.js | 2 +- .../index_pattern_select/combo_box_select.tsx | 2 +- .../field_text_select.tsx | 0 .../lib/index_pattern_select/index.ts | 0 .../index_pattern_select.tsx | 2 +- .../switch_mode_popover.tsx | 0 .../lib/index_pattern_select/types.ts | 2 +- .../lib/label_date_formatter.test.ts | 0 .../components/lib/label_date_formatter.ts | 0 .../components/lib/new_metric_agg_fn.ts | 2 +- .../components/lib/new_series_fn.js | 0 .../components/lib/re_id_series.js | 0 .../components/lib/re_id_series.test.js | 0 .../application/components/lib/reorder.ts | 0 .../components/lib/replace_vars.test.ts | 0 .../components/lib/replace_vars.ts | 0 .../components/lib/series_change_handler.js | 0 .../application/components/lib/stacked.js | 0 .../components/lib/tick_formatter.js | 0 .../components/lib/tick_formatter.test.js | 6 +- .../application/components/lib/types.ts | 0 .../application/components/markdown_editor.js | 2 +- .../components/palette_picker.test.tsx | 2 +- .../application/components/palette_picker.tsx | 0 .../components/panel_config/_index.scss | 0 .../panel_config/_panel_config.scss | 0 .../components/panel_config/gauge.test.tsx | 0 .../components/panel_config/gauge.tsx | 0 .../components/panel_config/index.ts | 0 .../components/panel_config/markdown.tsx | 2 +- .../components/panel_config/metric.tsx | 0 .../components/panel_config/panel_config.tsx | 0 .../components/panel_config/table.tsx | 2 +- .../panel_config/timeseries.test.tsx | 0 .../components/panel_config/timeseries.tsx | 0 .../components/panel_config/top_n.tsx | 0 .../components/panel_config/types.ts | 0 .../components/query_bar_wrapper.tsx | 2 +- .../public/application/components/series.js | 0 .../application/components/series_config.js | 0 ...fig_query_bar_with_ignore_global_filter.js | 0 .../components/series_drag_handler.tsx | 0 .../application/components/series_editor.js | 0 .../public/application/components/split.js | 0 .../splits/__snapshots__/terms.test.js.snap | 0 .../components/splits/everything.js | 0 .../application/components/splits/filter.js | 0 .../components/splits/filter_items.js | 0 .../application/components/splits/filters.js | 0 .../components/splits/group_by_select.js | 0 .../application/components/splits/terms.js | 2 +- .../components/splits/terms.test.js | 0 .../components/splits/unsupported_split.js | 0 .../application/components/svg/bomb_icon.js | 0 .../application/components/svg/fire_icon.js | 0 .../components/timeseries_visualization.scss | 0 .../components/timeseries_visualization.tsx | 2 +- .../use_index_patter_mode_callout.tsx | 0 .../application/components/vis_editor.tsx | 10 +- .../components/vis_editor_lazy.tsx | 0 .../components/vis_editor_visualization.js | 0 .../application/components/vis_picker.tsx | 0 .../components/vis_types/_index.scss | 0 .../components/vis_types/_vis_types.scss | 0 .../components/vis_types/gauge/series.js | 0 .../components/vis_types/gauge/series.test.js | 0 .../components/vis_types/gauge/vis.js | 0 .../application/components/vis_types/index.ts | 2 +- .../vis_types/markdown/_markdown.scss | 0 .../components/vis_types/markdown/series.js | 0 .../components/vis_types/markdown/vis.js | 2 +- .../components/vis_types/metric/series.js | 0 .../vis_types/metric/series.test.js | 0 .../components/vis_types/metric/vis.js | 0 .../components/vis_types/table/config.js | 0 .../components/vis_types/table/is_sortable.js | 0 .../components/vis_types/table/series.js | 0 .../components/vis_types/table/vis.js | 4 +- .../components/vis_types/timeseries/config.js | 0 .../components/vis_types/timeseries/series.js | 0 .../components/vis_types/timeseries/vis.js | 2 +- .../vis_types/timeseries/vis.test.js | 8 +- .../components/vis_types/top_n/series.js | 0 .../components/vis_types/top_n/vis.js | 0 .../application/components/vis_with_splits.js | 0 .../application/components/yes_no.test.tsx | 0 .../public/application/components/yes_no.tsx | 0 .../contexts/form_validation_context.ts | 0 .../contexts/panel_model_context.ts | 0 .../contexts/query_input_bar_context.ts | 0 .../application/contexts/vis_data_context.ts | 0 .../public/application/editor_controller.tsx | 0 .../timeseries}/public/application/index.scss | 0 .../application/lib/check_ui_restrictions.js | 0 .../lib/compute_gradient_final_color.test.ts | 0 .../lib/compute_gradient_final_color.ts | 0 .../public/application/lib/fetch_fields.ts | 0 .../lib/get_split_by_terms_color.test.ts | 2 +- .../lib/get_split_by_terms_color.ts | 0 .../public/application/lib/get_timezone.ts | 0 .../public/application/lib/index.ts | 0 .../public/application/lib/rainbow_colors.ts | 0 .../public/application/lib/set_is_reversed.js | 0 .../visualizations/constants/chart.ts | 0 .../visualizations/constants/icons.ts | 0 .../visualizations/constants/index.ts | 0 .../visualizations/lib/calc_dimensions.js | 0 .../lib/calculate_coordinates.js | 0 .../visualizations/lib/get_value_by.js | 0 .../visualizations/views/_annotation.scss | 0 .../visualizations/views/_gauge.scss | 0 .../visualizations/views/_index.scss | 0 .../visualizations/views/_metric.scss | 0 .../visualizations/views/_top_n.scss | 0 .../visualizations/views/annotation.js | 0 .../application/visualizations/views/gauge.js | 0 .../visualizations/views/gauge_vis.js | 0 .../visualizations/views/metric.js | 0 .../__snapshots__/area_decorator.test.js.snap | 0 .../__snapshots__/bar_decorator.test.js.snap | 0 .../timeseries/decorators/area_decorator.js | 0 .../decorators/area_decorator.test.js | 0 .../timeseries/decorators/bar_decorator.js | 0 .../decorators/bar_decorator.test.js | 0 .../visualizations/views/timeseries/index.js | 2 +- .../model/__snapshots__/charts.test.js.snap | 0 .../views/timeseries/model/charts.js | 0 .../views/timeseries/model/charts.test.js | 0 .../__snapshots__/series_styles.test.js.snap | 0 .../utils/series_domain_calculation.ts | 0 .../utils/series_domain_calculations.test.ts | 2 +- .../views/timeseries/utils/series_styles.js | 0 .../timeseries/utils/series_styles.test.js | 0 .../views/timeseries/utils/stack_format.js | 0 .../timeseries/utils/stack_format.test.js | 0 .../views/timeseries/utils/theme.test.ts | 0 .../views/timeseries/utils/theme.ts | 0 .../application/visualizations/views/top_n.js | 0 .../timeseries}/public/index.ts | 2 +- .../timeseries}/public/metrics_fn.ts | 4 +- .../timeseries}/public/metrics_type.ts | 2 +- .../timeseries}/public/plugin.ts | 10 +- .../timeseries}/public/request_handler.ts | 2 +- .../timeseries}/public/services.ts | 6 +- .../timeseries}/public/test_utils/index.ts | 0 .../public/timeseries_vis_renderer.tsx | 4 +- .../timeseries}/public/to_ast.ts | 4 +- .../timeseries}/public/types.ts | 0 .../timeseries}/server/config.ts | 0 .../timeseries}/server/index.ts | 0 .../timeseries}/server/lib/get_fields.ts | 0 .../timeseries}/server/lib/get_vis_data.ts | 0 .../default_search_capabilities.test.ts | 0 .../default_search_capabilities.ts | 0 .../rollup_search_capabilities.test.ts | 0 .../rollup_search_capabilities.ts | 0 .../server/lib/search_strategies/index.ts | 0 .../lib/cached_index_pattern_fetcher.test.ts | 0 .../lib/cached_index_pattern_fetcher.ts | 2 +- .../search_strategies/lib/fields_fetcher.ts | 2 +- .../lib/interval_helper.test.ts | 0 .../search_strategies/lib/interval_helper.ts | 0 .../search_strategies_registry.test.ts | 0 .../search_strategy_registry.ts | 0 .../abstract_search_strategy.test.ts | 4 +- .../strategies/abstract_search_strategy.ts | 2 +- .../default_search_strategy.test.ts | 0 .../strategies/default_search_strategy.ts | 2 +- .../lib/search_strategies/strategies/index.ts | 0 .../strategies/rollup_search_strategy.test.ts | 2 +- .../strategies/rollup_search_strategy.ts | 5 +- .../annotations/build_request_body.ts | 0 .../annotations/get_request_params.ts | 0 .../vis_data/build_processor_function.test.ts | 0 .../lib/vis_data/build_processor_function.ts | 0 .../server/lib/vis_data/get_annotations.ts | 0 .../get_interval_and_timefield.test.ts | 0 .../vis_data/get_interval_and_timefield.ts | 0 .../server/lib/vis_data/get_series_data.ts | 0 .../server/lib/vis_data/get_table_data.ts | 0 .../vis_data/handle_error_response.test.ts | 0 .../lib/vis_data/handle_error_response.ts | 0 .../bucket_transform.test.js.snap | 0 .../lib/vis_data/helpers/bucket_transform.js | 0 .../vis_data/helpers/bucket_transform.test.js | 0 .../server/lib/vis_data/helpers/check_aggs.ts | 0 .../server/lib/vis_data/helpers/format_key.ts | 0 .../lib/vis_data/helpers/get_active_series.ts | 0 .../lib/vis_data/helpers/get_agg_value.js | 2 +- .../vis_data/helpers/get_agg_value.test.js | 0 .../vis_data/helpers/get_bucket_size.test.ts | 0 .../lib/vis_data/helpers/get_bucket_size.ts | 2 +- .../vis_data/helpers/get_buckets_path.test.ts | 0 .../lib/vis_data/helpers/get_buckets_path.ts | 2 +- .../helpers/get_default_decoration.js | 0 .../helpers/get_default_decoration.test.js | 0 .../helpers/get_es_query_uisettings.ts | 2 +- .../vis_data/helpers/get_last_metric.test.ts | 0 .../lib/vis_data/helpers/get_last_metric.ts | 0 .../vis_data/helpers/get_sibling_agg_value.js | 0 .../helpers/get_sibling_agg_value.test.js | 0 .../lib/vis_data/helpers/get_splits.test.js | 0 .../server/lib/vis_data/helpers/get_splits.ts | 0 .../vis_data/helpers/get_timerange.test.ts | 0 .../lib/vis_data/helpers/get_timerange.ts | 0 .../vis_data/helpers/get_timerange_mode.ts | 0 .../server/lib/vis_data/helpers/index.ts | 0 .../helpers/map_empty_to_zero.test.ts | 2 +- .../lib/vis_data/helpers/map_empty_to_zero.ts | 2 +- .../lib/vis_data/helpers/moving_fn_scripts.js | 0 .../helpers/moving_fn_scripts.test.js | 0 .../server/lib/vis_data/helpers/overwrite.ts | 0 .../lib/vis_data/helpers/parse_interval.js | 0 .../lib/vis_data/helpers/timestamp.test.ts | 0 .../server/lib/vis_data/helpers/timestamp.ts | 0 .../vis_data/helpers/unit_to_seconds.test.ts | 0 .../lib/vis_data/helpers/unit_to_seconds.ts | 0 .../server/lib/vis_data/offset_time.js | 0 .../server/lib/vis_data/offset_time.test.js | 0 .../annotations/date_histogram.ts | 2 +- .../request_processors/annotations/index.ts | 0 .../request_processors/annotations/query.ts | 2 +- .../annotations/top_hits.ts | 0 .../request_processors/annotations/types.ts | 0 .../series/date_histogram.js | 2 +- .../series/date_histogram.test.js | 2 +- .../series/filter_ratios.js | 2 +- .../series/filter_ratios.test.js | 0 .../request_processors/series/index.js | 0 .../series/metric_buckets.js | 0 .../series/metric_buckets.test.js | 0 .../series/normalize_query.js | 0 .../series/normalize_query.test.js | 0 .../series/positive_rate.js | 2 +- .../series/positive_rate.test.js | 0 .../request_processors/series/query.js | 2 +- .../request_processors/series/query.test.js | 0 .../series/sibling_buckets.js | 0 .../series/sibling_buckets.test.js | 0 .../series/split_by_everything.js | 0 .../series/split_by_everything.test.js | 0 .../series/split_by_filter.js | 2 +- .../series/split_by_filter.test.js | 0 .../series/split_by_filters.js | 2 +- .../series/split_by_filters.test.js | 0 .../series/split_by_terms.js | 0 .../series/split_by_terms.test.js | 0 .../table/calculate_agg_root.ts | 0 .../table/date_histogram.ts | 2 +- .../request_processors/table/filter_ratios.ts | 2 +- .../request_processors/table/index.ts | 0 .../table/metric_buckets.ts | 0 .../table/normalize_query.test.ts | 0 .../table/normalize_query.ts | 0 .../request_processors/table/pivot.ts | 0 .../request_processors/table/positive_rate.ts | 2 +- .../request_processors/table/query.ts | 2 +- .../table/sibling_buckets.ts | 0 .../table/split_by_everything.ts | 2 +- .../table/split_by_terms.ts | 2 +- .../request_processors/table/types.ts | 2 +- .../lib/vis_data/request_processors/types.ts | 0 .../annotations/buckets.ts | 0 .../annotations/filter.test.ts | 0 .../response_processors/annotations/filter.ts | 0 .../response_processors/annotations/index.ts | 0 .../response_processors/series/_series_agg.js | 0 .../series/_series_agg.test.js | 0 .../series/drop_last_bucket.js | 0 .../series/format_label.ts | 2 +- .../response_processors/series/index.js | 0 .../response_processors/series/math.js | 0 .../response_processors/series/math.test.js | 0 .../response_processors/series/percentile.js | 0 .../series/percentile.test.js | 0 .../series/percentile_rank.js | 0 .../series/percentile_rank.test.ts | 0 .../response_processors/series/series_agg.js | 0 .../series/series_agg.test.js | 0 .../series/std_deviation_bands.js | 0 .../series/std_deviation_bands.test.js | 0 .../series/std_deviation_sibling.js | 0 .../series/std_deviation_sibling.test.js | 0 .../response_processors/series/std_metric.js | 0 .../series/std_metric.test.js | 0 .../response_processors/series/std_sibling.js | 0 .../series/std_sibling.test.js | 0 .../response_processors/series/time_shift.js | 0 .../series/time_shift.test.js | 0 .../response_processors/table/_series_agg.js | 0 .../table/drop_last_bucket.ts | 0 .../response_processors/table/index.ts | 0 .../response_processors/table/math.ts | 0 .../response_processors/table/percentile.ts | 0 .../table/percentile_rank.ts | 0 .../response_processors/table/series_agg.ts | 0 .../response_processors/table/std_metric.ts | 0 .../response_processors/table/std_sibling.ts | 0 .../response_processors/table/types.ts | 0 .../series/build_request_body.test.ts | 0 .../lib/vis_data/series/build_request_body.ts | 0 .../lib/vis_data/series/get_request_params.ts | 0 .../vis_data/series/handle_response_body.ts | 2 +- .../lib/vis_data/table/build_request_body.ts | 0 .../lib/vis_data/table/build_response_body.ts | 0 .../lib/vis_data/table/process_bucket.test.ts | 0 .../lib/vis_data/table/process_bucket.ts | 0 .../timeseries}/server/plugin.ts | 8 +- .../timeseries}/server/routes/fields.ts | 0 .../timeseries}/server/routes/vis.ts | 0 .../timeseries}/server/types.ts | 4 +- .../timeseries}/server/ui_settings.ts | 0 .../get_usage_collector.mock.ts | 0 .../get_usage_collector.test.ts | 7 +- .../usage_collector/get_usage_collector.ts | 6 +- .../server/usage_collector/index.ts | 0 .../register_timeseries_collector.test.ts | 4 +- .../register_timeseries_collector.ts | 2 +- .../vis_types/timeseries/tsconfig.json | 26 ++ .../lib/adapters/framework/adapter_types.ts | 2 +- .../framework/kibana_framework_adapter.ts | 2 +- .../metrics/kibana_metrics_adapter.ts | 2 +- x-pack/plugins/infra/tsconfig.json | 2 +- x-pack/plugins/rollup/server/types.ts | 2 +- x-pack/plugins/rollup/tsconfig.json | 2 +- 474 files changed, 420 insertions(+), 414 deletions(-) delete mode 100644 src/plugins/vis_type_timeseries/tsconfig.json rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/__mocks__/index_patterns_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/agg_utils.test.ts (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/agg_utils.ts (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/basic_aggs.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/calculate_label.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/calculate_label.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/constants.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/empty_label.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/metric_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/model_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/panel_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/timerange_data_modes.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/errors.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/fields_utils.test.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/fields_utils.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/index_patterns_utils.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/index_patterns_utils.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/interval_regexp.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/interval_regexp.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/last_value_utils.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/last_value_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/operators_utils.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/operators_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/to_percentile_number.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/color_rules.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/index.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/panel_model.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/vis_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/ui_restrictions.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/validate_interval.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/vis_data_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/jest.config.js (72%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/kibana.json (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_mixins.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_tvb_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_variables.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_annotations_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_color_picker.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_color_rules.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_error.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_markdown_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_no_data.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_series_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_editor_visualization.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_picker.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_with_splits.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/add_delete_buttons.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/add_delete_buttons.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/_agg_row.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_row.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_select.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/aggs.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/calculation.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/cumulative_sum.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/derivative.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/field_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/filter_ratio.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/filter_ratio.test.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/histogram_support.test.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/invalid_agg.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/math.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/metric_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/moving_average.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_hdr.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/multi_value_row.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/percentile_rank.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_ui.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_ui.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/positive_only.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/positive_rate.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/serial_diff.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/static.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_deviation.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/top_hit.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/vars.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/annotation_row.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/annotations_editor.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_picker.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_rules.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_rules.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/data_format_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/error.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/icon_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/icon_select.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/index_pattern.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_indicator.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_popover.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_popover.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/agg_to_component.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/calculate_siblings.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/calculate_siblings.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_numeric_metric.test.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_numeric_metric.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_series_have_same_formatters.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_series_have_same_formatters.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/collection_actions.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/collection_actions.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_datatable.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_datatable.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_vars.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_vars.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_change_handler.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_field_formatter.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_field_formatter.ts (86%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_interval_based_formatter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_number_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_number_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_select_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_select_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_text_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_text_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/durations.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/durations.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_axis_label_string.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_axis_label_string.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_click_filter_data.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_click_filter_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_default_query_language.ts (89%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_display_name.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_formatter_type.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_formatter_type.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_interval.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_metrics_field.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_metrics_field.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_supported_fields_by_metric_type.js (87%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_supported_fields_by_metric_type.test.js (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/combo_box_select.tsx (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/field_text_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/index_pattern_select.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/types.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/label_date_formatter.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/label_date_formatter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/new_metric_agg_fn.ts (89%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/new_series_fn.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/re_id_series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/re_id_series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/reorder.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/replace_vars.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/replace_vars.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/series_change_handler.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/stacked.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/tick_formatter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/tick_formatter.test.js (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/markdown_editor.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/palette_picker.test.tsx (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/palette_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/_panel_config.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/gauge.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/gauge.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/markdown.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/metric.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/panel_config.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/table.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/timeseries.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/timeseries.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/top_n.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/query_bar_wrapper.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_config_query_bar_with_ignore_global_filter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_drag_handler.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_editor.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/split.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/__snapshots__/terms.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/everything.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filter_items.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filters.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/group_by_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/terms.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/terms.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/unsupported_split.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/svg/bomb_icon.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/svg/fire_icon.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/timeseries_visualization.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/timeseries_visualization.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/use_index_patter_mode_callout.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor.tsx (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor_lazy.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor_visualization.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/_vis_types.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/index.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/_markdown.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/vis.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/is_sortable.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/vis.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/vis.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/vis.test.js (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/top_n/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/top_n/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_with_splits.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/yes_no.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/yes_no.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/form_validation_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/panel_model_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/query_input_bar_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/vis_data_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/editor_controller.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/check_ui_restrictions.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/compute_gradient_final_color.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/compute_gradient_final_color.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/fetch_fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_split_by_terms_color.test.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_split_by_terms_color.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_timezone.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/rainbow_colors.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/set_is_reversed.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/chart.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/icons.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/calc_dimensions.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/calculate_coordinates.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/get_value_by.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_annotation.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_gauge.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_metric.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_top_n.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/annotation.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/gauge.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/gauge_vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/metric.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/area_decorator.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/bar_decorator.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/index.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/charts.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/charts.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_styles.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_styles.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/stack_format.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/stack_format.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/theme.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/theme.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/top_n.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/index.ts (88%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/metrics_fn.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/metrics_type.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/plugin.ts (86%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/request_handler.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/services.ts (84%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/test_utils/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/timeseries_vis_renderer.tsx (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/to_ast.ts (91%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/config.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/get_fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/get_vis_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/default_search_capabilities.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/fields_fetcher.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/interval_helper.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/interval_helper.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/search_strategies_registry.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/search_strategy_registry.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/abstract_search_strategy.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/default_search_strategy.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/default_search_strategy.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/rollup_search_strategy.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/annotations/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/annotations/get_request_params.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/build_processor_function.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/build_processor_function.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_annotations.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_interval_and_timefield.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_interval_and_timefield.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_series_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_table_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/handle_error_response.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/handle_error_response.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/bucket_transform.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/bucket_transform.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/check_aggs.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/format_key.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_active_series.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_agg_value.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_agg_value.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_bucket_size.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_bucket_size.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_buckets_path.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_buckets_path.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_default_decoration.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_default_decoration.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_es_query_uisettings.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_last_metric.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_last_metric.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_sibling_agg_value.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_sibling_agg_value.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_splits.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_splits.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange_mode.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/map_empty_to_zero.test.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/map_empty_to_zero.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/moving_fn_scripts.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/moving_fn_scripts.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/overwrite.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/parse_interval.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/timestamp.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/timestamp.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/unit_to_seconds.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/unit_to_seconds.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/offset_time.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/offset_time.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/date_histogram.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/query.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/top_hits.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/date_histogram.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/date_histogram.test.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/filter_ratios.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/filter_ratios.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/metric_buckets.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/metric_buckets.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/normalize_query.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/normalize_query.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/positive_rate.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/positive_rate.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/query.js (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/query.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/sibling_buckets.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/sibling_buckets.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_everything.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_everything.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filter.js (92%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filter.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filters.js (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filters.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_terms.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_terms.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/calculate_agg_root.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/date_histogram.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/filter_ratios.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/metric_buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/normalize_query.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/normalize_query.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/pivot.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/positive_rate.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/query.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/sibling_buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/split_by_everything.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/split_by_terms.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/types.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/filter.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/filter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/_series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/_series_agg.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/drop_last_bucket.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/format_label.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/math.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/math.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile_rank.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile_rank.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/series_agg.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_bands.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_metric.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_metric.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_sibling.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/time_shift.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/time_shift.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/_series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/drop_last_bucket.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/math.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/percentile.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/percentile_rank.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/series_agg.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/std_metric.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/std_sibling.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/build_request_body.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/get_request_params.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/handle_response_body.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/build_response_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/process_bucket.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/process_bucket.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/plugin.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/routes/fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/routes/vis.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/types.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/ui_settings.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.mock.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.test.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/register_timeseries_collector.test.ts (92%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/register_timeseries_collector.ts (92%) create mode 100644 src/plugins/vis_types/timeseries/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 30bc9d732267..83afc2726324 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1502,7 +1502,7 @@ module.exports = { * TSVB overrides */ { - files: ['src/plugins/vis_type_timeseries/**/*.{js,mjs,ts,tsx}'], + files: ['src/plugins/vis_types/timeseries/**/*.{js,mjs,ts,tsx}'], rules: { 'import/no-default-export': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 783198156922..6ae834b58fc8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ /src/plugins/vis_types/table/ @elastic/kibana-vis-editors /src/plugins/vis_types/tagcloud/ @elastic/kibana-vis-editors /src/plugins/vis_types/timelion/ @elastic/kibana-vis-editors -/src/plugins/vis_type_timeseries/ @elastic/kibana-vis-editors +/src/plugins/vis_types/timeseries/ @elastic/kibana-vis-editors /src/plugins/vis_types/vega/ @elastic/kibana-vis-editors /src/plugins/vis_types/vislib/ @elastic/kibana-vis-editors /src/plugins/vis_types/xy/ @elastic/kibana-vis-editors diff --git a/.i18nrc.json b/.i18nrc.json index 90afa274c6e6..4107772e421c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -63,7 +63,7 @@ "visTypeMetric": "src/plugins/vis_types/metric", "visTypeTable": "src/plugins/vis_types/table", "visTypeTagCloud": "src/plugins/vis_types/tagcloud", - "visTypeTimeseries": "src/plugins/vis_type_timeseries", + "visTypeTimeseries": "src/plugins/vis_types/timeseries", "visTypeVega": "src/plugins/vis_types/vega", "visTypeVislib": "src/plugins/vis_types/vislib", "visTypeXy": "src/plugins/vis_types/xy", diff --git a/api_docs/data.json b/api_docs/data.json index e62ddb5e30f1..3012f68b4c0f 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -2716,19 +2716,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -2924,11 +2924,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -3076,35 +3076,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -4972,15 +4972,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -5144,15 +5144,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -5176,15 +5176,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -6581,27 +6581,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -6874,51 +6874,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -6946,39 +6946,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -6990,27 +6990,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -14937,11 +14937,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -16879,11 +16879,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", @@ -24265,35 +24265,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts" + "path": "src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/metrics_type.ts" + "path": "src/plugins/vis_types/timeseries/public/metrics_type.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx" }, { "plugin": "visTypeVega", @@ -24483,7 +24483,7 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/plugin.ts" + "path": "src/plugins/vis_types/timeseries/public/plugin.ts" }, { "plugin": "visTypeMetric", @@ -25018,19 +25018,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -25226,11 +25226,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -25378,35 +25378,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -27274,15 +27274,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -27446,15 +27446,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -27478,15 +27478,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -28883,27 +28883,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -29176,51 +29176,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -29248,39 +29248,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -29292,27 +29292,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -29373,51 +29373,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -29445,39 +29445,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -29489,27 +29489,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -30753,11 +30753,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -32456,11 +32456,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", @@ -37439,11 +37439,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -39172,11 +39172,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", diff --git a/api_docs/data_index_patterns.json b/api_docs/data_index_patterns.json index cf621b241397..3ebbf2429948 100644 --- a/api_docs/data_index_patterns.json +++ b/api_docs/data_index_patterns.json @@ -1111,7 +1111,7 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "graph", @@ -1147,7 +1147,7 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts" + "path": "src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts" } ], "children": [], @@ -3639,19 +3639,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -3847,11 +3847,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -3999,35 +3999,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -5895,15 +5895,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -6067,15 +6067,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -6099,15 +6099,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -7504,27 +7504,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -7797,51 +7797,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -7869,39 +7869,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -7913,27 +7913,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 99f241e81384..67a9e90c7460 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -926,24 +926,24 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts#:~:text=indexPatterns), [combo_box_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx#:~:text=indexPatterns), [query_bar_wrapper.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx#:~:text=indexPatterns), [annotation_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx#:~:text=indexPatterns), [metrics_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/metrics_type.ts#:~:text=indexPatterns), [convert_series_to_datatable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns) | - | -| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/plugin.ts#:~:text=fieldFormats) | - | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=getNonScriptedFields), [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts#:~:text=getNonScriptedFields) | 8.1 | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 44 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts#:~:text=indexPatterns), [combo_box_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx#:~:text=indexPatterns), [query_bar_wrapper.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx#:~:text=indexPatterns), [annotation_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx#:~:text=indexPatterns), [metrics_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/metrics_type.ts#:~:text=indexPatterns), [convert_series_to_datatable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns) | - | +| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/plugin.ts#:~:text=fieldFormats) | - | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=getNonScriptedFields), [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts#:~:text=getNonScriptedFields) | 8.1 | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 44 more | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | diff --git a/api_docs/vis_type_timeseries.json b/api_docs/vis_type_timeseries.json index 01b211e3da90..885f037b00a2 100644 --- a/api_docs/vis_type_timeseries.json +++ b/api_docs/vis_type_timeseries.json @@ -24,7 +24,7 @@ ") => data is ", "SeriesData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "children": [ { @@ -37,7 +37,7 @@ "signature": [ "TimeseriesVisData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "isRequired": true } @@ -58,7 +58,7 @@ ") => data is ", "TableData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "children": [ { @@ -71,7 +71,7 @@ "signature": [ "TimeseriesVisData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "isRequired": true } @@ -95,7 +95,7 @@ " | ", "TableData" ], - "path": "src/plugins/vis_type_timeseries/common/types/vis_data.ts", + "path": "src/plugins/vis_types/timeseries/common/types/vis_data.ts", "deprecated": false, "initialIsOpen": false } @@ -108,7 +108,7 @@ "tags": [], "label": "VisTypeTimeseriesSetup", "description": [], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "children": [ { @@ -133,7 +133,7 @@ "TimeseriesVisData", ">" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "children": [ { @@ -146,7 +146,7 @@ "signature": [ "DataRequestHandlerContext" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true }, @@ -167,7 +167,7 @@ }, "" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true }, @@ -181,7 +181,7 @@ "signature": [ "any" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true } diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 850b80c20e09..1ab7ab60d4a8 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -297,7 +297,7 @@ The plugin exposes the static DefaultEditorController class to consume. |Contains the timelion visualization and the timelion backend. -|{kib-repo}blob/{branch}/src/plugins/vis_type_timeseries[visTypeTimeseries] +|{kib-repo}blob/{branch}/src/plugins/vis_types/timeseries[visTypeTimeseries] |WARNING: Missing README. diff --git a/src/plugins/vis_type_timeseries/tsconfig.json b/src/plugins/vis_type_timeseries/tsconfig.json deleted file mode 100644 index 68097d8cff78..000000000000 --- a/src/plugins/vis_type_timeseries/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "*.ts" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../charts/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../expressions/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../visualize/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../kibana_react/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" }, - ] -} diff --git a/src/plugins/vis_type_timeseries/common/__mocks__/index_patterns_utils.ts b/src/plugins/vis_types/timeseries/common/__mocks__/index_patterns_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/__mocks__/index_patterns_utils.ts rename to src/plugins/vis_types/timeseries/common/__mocks__/index_patterns_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts b/src/plugins/vis_types/timeseries/common/agg_utils.test.ts similarity index 99% rename from src/plugins/vis_type_timeseries/common/agg_utils.test.ts rename to src/plugins/vis_types/timeseries/common/agg_utils.test.ts index 63d81e2c43d4..21fa870c2ca0 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/agg_utils.test.ts @@ -13,7 +13,7 @@ import { getAggsByPredicate, getAggsByType, } from './agg_utils'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.ts b/src/plugins/vis_types/timeseries/common/agg_utils.ts similarity index 99% rename from src/plugins/vis_type_timeseries/common/agg_utils.ts rename to src/plugins/vis_types/timeseries/common/agg_utils.ts index 2f0488bdc4db..71f13542b10f 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.ts +++ b/src/plugins/vis_types/timeseries/common/agg_utils.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { filter } from 'lodash'; import { Assign } from 'utility-types'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric, MetricType } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/basic_aggs.ts b/src/plugins/vis_types/timeseries/common/basic_aggs.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/basic_aggs.ts rename to src/plugins/vis_types/timeseries/common/basic_aggs.ts diff --git a/src/plugins/vis_type_timeseries/common/calculate_label.test.ts b/src/plugins/vis_types/timeseries/common/calculate_label.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/calculate_label.test.ts rename to src/plugins/vis_types/timeseries/common/calculate_label.test.ts diff --git a/src/plugins/vis_type_timeseries/common/calculate_label.ts b/src/plugins/vis_types/timeseries/common/calculate_label.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/calculate_label.ts rename to src/plugins/vis_types/timeseries/common/calculate_label.ts index d054698536b5..e5cf053d310a 100644 --- a/src/plugins/vis_type_timeseries/common/calculate_label.ts +++ b/src/plugins/vis_types/timeseries/common/calculate_label.ts @@ -10,7 +10,7 @@ import { includes, startsWith } from 'lodash'; import { i18n } from '@kbn/i18n'; import { getMetricLabel } from './agg_utils'; import { extractFieldLabel } from './fields_utils'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric, SanitizedFieldType } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/constants.ts b/src/plugins/vis_types/timeseries/common/constants.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/constants.ts rename to src/plugins/vis_types/timeseries/common/constants.ts diff --git a/src/plugins/vis_type_timeseries/common/empty_label.ts b/src/plugins/vis_types/timeseries/common/empty_label.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/empty_label.ts rename to src/plugins/vis_types/timeseries/common/empty_label.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/index.ts b/src/plugins/vis_types/timeseries/common/enums/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/index.ts rename to src/plugins/vis_types/timeseries/common/enums/index.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/metric_types.ts b/src/plugins/vis_types/timeseries/common/enums/metric_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/metric_types.ts rename to src/plugins/vis_types/timeseries/common/enums/metric_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/model_types.ts b/src/plugins/vis_types/timeseries/common/enums/model_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/model_types.ts rename to src/plugins/vis_types/timeseries/common/enums/model_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/panel_types.ts b/src/plugins/vis_types/timeseries/common/enums/panel_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/panel_types.ts rename to src/plugins/vis_types/timeseries/common/enums/panel_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/timerange_data_modes.ts b/src/plugins/vis_types/timeseries/common/enums/timerange_data_modes.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/timerange_data_modes.ts rename to src/plugins/vis_types/timeseries/common/enums/timerange_data_modes.ts diff --git a/src/plugins/vis_type_timeseries/common/errors.ts b/src/plugins/vis_types/timeseries/common/errors.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/errors.ts rename to src/plugins/vis_types/timeseries/common/errors.ts diff --git a/src/plugins/vis_type_timeseries/common/fields_utils.test.ts b/src/plugins/vis_types/timeseries/common/fields_utils.test.ts similarity index 96% rename from src/plugins/vis_type_timeseries/common/fields_utils.test.ts rename to src/plugins/vis_types/timeseries/common/fields_utils.test.ts index f056c38b0c0c..228dfbfd2db9 100644 --- a/src/plugins/vis_type_timeseries/common/fields_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.test.ts @@ -7,7 +7,7 @@ */ import { toSanitizedFieldType } from './fields_utils'; -import type { FieldSpec } from '../../data/common'; +import type { FieldSpec } from '../../../data/common'; describe('fields_utils', () => { describe('toSanitizedFieldType', () => { diff --git a/src/plugins/vis_type_timeseries/common/fields_utils.ts b/src/plugins/vis_types/timeseries/common/fields_utils.ts similarity index 93% rename from src/plugins/vis_type_timeseries/common/fields_utils.ts rename to src/plugins/vis_types/timeseries/common/fields_utils.ts index 1af0340dfa52..d6987b9cdae9 100644 --- a/src/plugins/vis_type_timeseries/common/fields_utils.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { FieldSpec } from '../../data/common'; -import { isNestedField } from '../../data/common'; +import { FieldSpec } from '../../../data/common'; +import { isNestedField } from '../../../data/common'; import { FetchedIndexPattern, SanitizedFieldType } from './types'; import { FieldNotFoundError } from './errors'; diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts b/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts rename to src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts index cac607f7c0f9..e9f3be64079a 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts @@ -12,7 +12,7 @@ import { fetchIndexPattern, } from './index_patterns_utils'; import { Panel } from './types'; -import { IndexPattern, IndexPatternsService } from '../../data/common'; +import { IndexPattern, IndexPatternsService } from '../../../data/common'; describe('isStringTypeIndexPattern', () => { test('should returns true on string-based index', () => { diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts b/src/plugins/vis_types/timeseries/common/index_patterns_utils.ts similarity index 97% rename from src/plugins/vis_type_timeseries/common/index_patterns_utils.ts rename to src/plugins/vis_types/timeseries/common/index_patterns_utils.ts index 1a8c277efbf7..0a65e9e16d13 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts +++ b/src/plugins/vis_types/timeseries/common/index_patterns_utils.ts @@ -8,7 +8,7 @@ import { uniq } from 'lodash'; import type { Panel, IndexPatternValue, FetchedIndexPattern } from '../common/types'; -import { IndexPatternsService } from '../../data/common'; +import { IndexPatternsService } from '../../../data/common'; export const isStringTypeIndexPattern = ( indexPatternValue: IndexPatternValue diff --git a/src/plugins/vis_type_timeseries/common/interval_regexp.test.ts b/src/plugins/vis_types/timeseries/common/interval_regexp.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/interval_regexp.test.ts rename to src/plugins/vis_types/timeseries/common/interval_regexp.test.ts diff --git a/src/plugins/vis_type_timeseries/common/interval_regexp.ts b/src/plugins/vis_types/timeseries/common/interval_regexp.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/interval_regexp.ts rename to src/plugins/vis_types/timeseries/common/interval_regexp.ts diff --git a/src/plugins/vis_type_timeseries/common/last_value_utils.test.ts b/src/plugins/vis_types/timeseries/common/last_value_utils.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/last_value_utils.test.ts rename to src/plugins/vis_types/timeseries/common/last_value_utils.test.ts diff --git a/src/plugins/vis_type_timeseries/common/last_value_utils.ts b/src/plugins/vis_types/timeseries/common/last_value_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/last_value_utils.ts rename to src/plugins/vis_types/timeseries/common/last_value_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/operators_utils.test.ts b/src/plugins/vis_types/timeseries/common/operators_utils.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/operators_utils.test.ts rename to src/plugins/vis_types/timeseries/common/operators_utils.test.ts diff --git a/src/plugins/vis_type_timeseries/common/operators_utils.ts b/src/plugins/vis_types/timeseries/common/operators_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/operators_utils.ts rename to src/plugins/vis_types/timeseries/common/operators_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/to_percentile_number.ts b/src/plugins/vis_types/timeseries/common/to_percentile_number.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/to_percentile_number.ts rename to src/plugins/vis_types/timeseries/common/to_percentile_number.ts diff --git a/src/plugins/vis_type_timeseries/common/types/color_rules.ts b/src/plugins/vis_types/timeseries/common/types/color_rules.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/types/color_rules.ts rename to src/plugins/vis_types/timeseries/common/types/color_rules.ts diff --git a/src/plugins/vis_type_timeseries/common/types/index.ts b/src/plugins/vis_types/timeseries/common/types/index.ts similarity index 95% rename from src/plugins/vis_type_timeseries/common/types/index.ts rename to src/plugins/vis_types/timeseries/common/types/index.ts index fb8e217fe704..123b6723d8cc 100644 --- a/src/plugins/vis_type_timeseries/common/types/index.ts +++ b/src/plugins/vis_types/timeseries/common/types/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, IndexPattern, Query } from '../../../data/common'; +import { Filter, IndexPattern, Query } from '../../../../data/common'; import { Panel } from './panel_model'; export { Metric, Series, Panel, MetricType } from './panel_model'; diff --git a/src/plugins/vis_type_timeseries/common/types/panel_model.ts b/src/plugins/vis_types/timeseries/common/types/panel_model.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/types/panel_model.ts rename to src/plugins/vis_types/timeseries/common/types/panel_model.ts index 6fd2e727ade3..f71602fdf044 100644 --- a/src/plugins/vis_type_timeseries/common/types/panel_model.ts +++ b/src/plugins/vis_types/timeseries/common/types/panel_model.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { METRIC_TYPES, Query } from '../../../data/common'; +import { METRIC_TYPES, Query } from '../../../../data/common'; import { PANEL_TYPES, TOOLTIP_MODES, TSVB_METRIC_TYPES } from '../enums'; import { IndexPatternValue, Annotation } from './index'; import { ColorRules, BackgroundColorRules, BarColorRules, GaugeColorRules } from './color_rules'; diff --git a/src/plugins/vis_type_timeseries/common/types/vis_data.ts b/src/plugins/vis_types/timeseries/common/types/vis_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/types/vis_data.ts rename to src/plugins/vis_types/timeseries/common/types/vis_data.ts diff --git a/src/plugins/vis_type_timeseries/common/ui_restrictions.ts b/src/plugins/vis_types/timeseries/common/ui_restrictions.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/ui_restrictions.ts rename to src/plugins/vis_types/timeseries/common/ui_restrictions.ts diff --git a/src/plugins/vis_type_timeseries/common/validate_interval.ts b/src/plugins/vis_types/timeseries/common/validate_interval.ts similarity index 93% rename from src/plugins/vis_type_timeseries/common/validate_interval.ts rename to src/plugins/vis_types/timeseries/common/validate_interval.ts index 7c7a4e7badfc..78a2410f905c 100644 --- a/src/plugins/vis_type_timeseries/common/validate_interval.ts +++ b/src/plugins/vis_types/timeseries/common/validate_interval.ts @@ -7,7 +7,7 @@ */ import { GTE_INTERVAL_RE } from './interval_regexp'; -import { parseInterval, TimeRangeBounds } from '../../data/common'; +import { parseInterval, TimeRangeBounds } from '../../../data/common'; import { ValidateIntervalError } from './errors'; export function validateInterval(bounds: TimeRangeBounds, interval: string, maxBuckets: number) { diff --git a/src/plugins/vis_type_timeseries/common/vis_data_utils.ts b/src/plugins/vis_types/timeseries/common/vis_data_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/vis_data_utils.ts rename to src/plugins/vis_types/timeseries/common/vis_data_utils.ts diff --git a/src/plugins/vis_type_timeseries/jest.config.js b/src/plugins/vis_types/timeseries/jest.config.js similarity index 72% rename from src/plugins/vis_type_timeseries/jest.config.js rename to src/plugins/vis_types/timeseries/jest.config.js index 3d4333675f7d..d6ddcaa3344b 100644 --- a/src/plugins/vis_type_timeseries/jest.config.js +++ b/src/plugins/vis_types/timeseries/jest.config.js @@ -8,11 +8,11 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/vis_type_timeseries'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/vis_type_timeseries', + rootDir: '../../../..', + roots: ['/src/plugins/vis_types/timeseries'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/vis_types/timeseries', coverageReporters: ['text', 'html'], collectCoverageFrom: [ - '/src/plugins/vis_type_timeseries/{common,public,server}/**/*.{js,ts,tsx}', + '/src/plugins/vis_types/timeseries/{common,public,server}/**/*.{js,ts,tsx}', ], }; diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_types/timeseries/kibana.json similarity index 100% rename from src/plugins/vis_type_timeseries/kibana.json rename to src/plugins/vis_types/timeseries/kibana.json diff --git a/src/plugins/vis_type_timeseries/public/application/_mixins.scss b/src/plugins/vis_types/timeseries/public/application/_mixins.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_mixins.scss rename to src/plugins/vis_types/timeseries/public/application/_mixins.scss diff --git a/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss b/src/plugins/vis_types/timeseries/public/application/_tvb_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss rename to src/plugins/vis_types/timeseries/public/application/_tvb_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/_variables.scss b/src/plugins/vis_types/timeseries/public/application/_variables.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_variables.scss rename to src/plugins/vis_types/timeseries/public/application/_variables.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_annotations_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_annotations_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_annotations_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_annotations_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_color_picker.scss b/src/plugins/vis_types/timeseries/public/application/components/_color_picker.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_color_picker.scss rename to src/plugins/vis_types/timeseries/public/application/components/_color_picker.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_color_rules.scss b/src/plugins/vis_types/timeseries/public/application/components/_color_rules.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_color_rules.scss rename to src/plugins/vis_types/timeseries/public/application/components/_color_rules.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_error.scss b/src/plugins/vis_types/timeseries/public/application/components/_error.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_error.scss rename to src/plugins/vis_types/timeseries/public/application/components/_error.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_markdown_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_markdown_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_markdown_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_markdown_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_no_data.scss b/src/plugins/vis_types/timeseries/public/application/components/_no_data.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_no_data.scss rename to src/plugins/vis_types/timeseries/public/application/components/_no_data.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_series_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_series_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_series_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_series_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_editor_visualization.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_editor_visualization.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_editor_visualization.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_editor_visualization.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_picker.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_picker.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_picker.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_picker.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_with_splits.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_with_splits.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.tsx b/src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.tsx rename to src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/_agg_row.scss b/src/plugins/vis_types/timeseries/public/application/components/aggs/_agg_row.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/_agg_row.scss rename to src/plugins/vis_types/timeseries/public/application/components/aggs/_agg_row.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/aggs/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/aggs/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_row.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_row.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/aggs.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/aggs.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/calculation.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/calculation.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/calculation.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/calculation.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/cumulative_sum.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/cumulative_sum.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/cumulative_sum.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/cumulative_sum.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/derivative.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/derivative.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/derivative.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/derivative.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/field_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/field_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js index 6a3f57e50279..59a372ccc110 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js @@ -24,7 +24,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { getSupportedFieldsByMetricType } from '../lib/get_supported_fields_by_metric_type'; import { getDataStart } from '../../../services'; import { QueryBarWrapper } from '../query_bar_wrapper'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js index 5648a8d3e713..bd9ceeeb7402 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js @@ -11,7 +11,7 @@ import { mountWithIntl } from '@kbn/test/jest'; import { FilterRatioAgg } from './filter_ratio'; import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils'; import { EuiComboBox } from '@elastic/eui'; -import { dataPluginMock } from '../../../../../data/public/mocks'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; import { setDataStart } from '../../../services'; jest.mock('../query_bar_wrapper', () => ({ diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js index c4a49a393acd..c131ba2ae804 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js @@ -12,7 +12,7 @@ import { Agg } from './agg'; import { FieldSelect } from './field_select'; import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils'; import { setDataStart } from '../../../services'; -import { dataPluginMock } from '../../../../../data/public/mocks'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; jest.mock('../query_bar_wrapper', () => ({ QueryBarWrapper: jest.fn(() => null), diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/invalid_agg.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/invalid_agg.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/invalid_agg.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/invalid_agg.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/math.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/math.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/math.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/math.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/metric_select.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/metric_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/metric_select.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/metric_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/moving_average.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/moving_average.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/moving_average.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/moving_average.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js index 94adb37de156..3e4159cdf42d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js @@ -23,7 +23,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; import { PercentileHdr } from './percentile_hdr'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_hdr.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_hdr.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/index.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/index.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/index.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx index 7f9634ff1584..8b71dec15f17 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx @@ -26,7 +26,7 @@ import { createNumberHandler } from '../../lib/create_number_handler'; import { AggRow } from '../agg_row'; import { PercentileRankValues } from './percentile_rank_values'; -import { KBN_FIELD_TYPES } from '../../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../data/public'; import type { Metric, Panel, SanitizedFieldType, Series } from '../../../../../common/types'; import { TSVB_DEFAULT_COLOR } from '../../../../../common/constants'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_only.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_only.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/positive_only.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/positive_only.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js index 09d9f2f1a62f..ea3f8542e859 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js @@ -26,7 +26,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const UNIT_OPTIONS = [ { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/serial_diff.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/serial_diff.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/serial_diff.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/serial_diff.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/series_agg.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/series_agg.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/series_agg.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/static.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/static.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/static.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/static.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_agg.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js index d4caa8a94652..728cda872026 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js @@ -25,7 +25,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const RESTRICT_FIELDS = KBN_FIELD_TYPES.NUMBER; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_sibling.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_sibling.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_sibling.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js index ade64fc3db8c..caf601dfab83 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js @@ -24,7 +24,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { PANEL_TYPES } from '../../../../common/enums'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/vars.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/vars.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/vars.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/vars.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx index 379c74d0d4bb..734bdfecac67 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx @@ -21,7 +21,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { getDataStart } from '../../services'; -import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES, Query } from '../../../../../../plugins/data/public'; import { AddDeleteButtons } from './add_delete_buttons'; import { ColorPicker } from './color_picker'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_types/timeseries/public/application/components/annotations_editor.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx rename to src/plugins/vis_types/timeseries/public/application/components/annotations_editor.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_picker.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_picker.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_rules.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_rules.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_rules.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_rules.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_rules.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_rules.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_rules.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_rules.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/data_format_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/data_format_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/error.js b/src/plugins/vis_types/timeseries/public/application/components/error.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/error.js rename to src/plugins/vis_types/timeseries/public/application/components/error.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap b/src/plugins/vis_types/timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.js b/src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.js rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.test.js b/src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.test.js rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js b/src/plugins/vis_types/timeseries/public/application/components/index_pattern.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/index_pattern.js rename to src/plugins/vis_types/timeseries/public/application/components/index_pattern.js index 181042462502..e5d09c745b52 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js +++ b/src/plugins/vis_types/timeseries/public/application/components/index_pattern.js @@ -26,7 +26,7 @@ import { createTextHandler } from './lib/create_text_handler'; import { IndexPatternSelect } from './lib/index_pattern_select'; import { YesNo } from './yes_no'; import { LastValueModePopover } from './last_value_mode_popover'; -import { KBN_FIELD_TYPES } from '../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../data/public'; import { FormValidationContext } from '../contexts/form_validation_context'; import { isGteInterval, validateReInterval, isAutoInterval } from './lib/get_interval'; import { i18n } from '@kbn/i18n'; @@ -36,7 +36,7 @@ import { AUTO_INTERVAL } from '../../../common/constants'; import { isTimerangeModeEnabled } from '../lib/check_ui_restrictions'; import { VisDataContext } from '../contexts/vis_data_context'; import { getDataStart, getUISettings } from '../../services'; -import { UI_SETTINGS } from '../../../../data/common'; +import { UI_SETTINGS } from '../../../../../data/common'; import { fetchIndexPattern } from '../../../common/index_patterns_utils'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_indicator.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_indicator.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.scss b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.scss rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.tsx rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/agg_to_component.js b/src/plugins/vis_types/timeseries/public/application/components/lib/agg_to_component.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/agg_to_component.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/agg_to_component.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.js b/src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts index 17827275f86d..eb6ea561fec8 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import { checkIfNumericMetric } from './check_if_numeric_metric'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts similarity index 94% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts index a70abaeac9f8..139c13d7ddbd 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts @@ -8,7 +8,7 @@ import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; import type { Metric, IndexPatternValue } from '../../../../common/types'; import type { VisFields } from '../../lib/fetch_fields'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts similarity index 94% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts index afa1216406ab..44715d1262d0 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts @@ -9,7 +9,7 @@ import { last, isEqual } from 'lodash'; import { DATA_FORMATTERS } from '../../../../common/enums'; import type { Series } from '../../../../common/types'; -import type { FieldFormatMap } from '../../../../../data/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; export const checkIfSeriesHaveSameFormatters = ( seriesModel: Series[], diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_change_handler.js b/src/plugins/vis_types/timeseries/public/application/components/lib/create_change_handler.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_change_handler.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_change_handler.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts index c56c6820fff4..5a6b6a18f67e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts @@ -7,7 +7,7 @@ */ import { createFieldFormatter } from './create_field_formatter'; -import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; +import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; import { setFieldFormats } from '../../../services'; import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; import type { CoreSetup } from 'kibana/public'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts similarity index 86% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts index 5cba549220f2..a7606895e84a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts @@ -9,9 +9,9 @@ import { isNumber } from 'lodash'; import { getFieldFormats } from '../../../services'; import { isEmptyValue, DISPLAY_EMPTY_VALUE } from '../../../../common/last_value_utils'; -import { FIELD_FORMAT_IDS } from '../../../../../field_formats/common'; -import type { FieldFormatMap } from '../../../../../data/common'; -import type { FieldFormatsContentType } from '../../../../../field_formats/common'; +import { FIELD_FORMAT_IDS } from '../../../../../../field_formats/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; +import type { FieldFormatsContentType } from '../../../../../../field_formats/common'; const DEFAULT_FIELD_FORMAT = { id: 'number' }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_interval_based_formatter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_interval_based_formatter.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/durations.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/durations.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/durations.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/durations.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts similarity index 89% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts index e6f65e71043c..3cb7ee0388f9 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts @@ -7,7 +7,7 @@ */ import { getUISettings } from '../../../services'; -import { UI_SETTINGS } from '../../../../../data/public'; +import { UI_SETTINGS } from '../../../../../../data/public'; export function getDefaultQueryLanguage() { return getUISettings().get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_display_name.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_display_name.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_display_name.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_display_name.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts index 4b232af299a1..ea86ef6dc7ae 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts @@ -9,7 +9,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { search } from '../../../../../../plugins/data/public'; +import { search } from '../../../../../../../plugins/data/public'; import { GTE_INTERVAL_RE } from '../../../../common/interval_regexp'; import { AUTO_INTERVAL } from '../../../../common/constants'; import { isVisTableData } from '../../../../common/vis_data_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js similarity index 87% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js index 7aa40d48994b..2909e7804b1b 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; export function getSupportedFieldsByMetricType(type) { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js similarity index 95% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js index c009146abb7b..878b4f7655f3 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js @@ -7,7 +7,7 @@ */ import { getSupportedFieldsByMetricType } from './get_supported_fields_by_metric_type'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; describe('getSupportedFieldsByMetricType', () => { const shouldHaveHistogramAndNumbers = (type) => diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx index 7111a63244c7..ad60aee29064 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx @@ -15,7 +15,7 @@ import { SwitchModePopover } from './switch_mode_popover'; import type { SelectIndexComponentProps } from './types'; import type { IndexPatternValue } from '../../../../../common/types'; -import type { IndexPatternsService } from '../../../../../../data/public'; +import type { IndexPatternsService } from '../../../../../../../data/public'; /** @internal **/ type IdsWithTitle = UnwrapPromise>; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx index 927b3c608c16..1029ac67cc43 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx @@ -19,7 +19,7 @@ import { ComboBoxSelect } from './combo_box_select'; import type { IndexPatternValue, FetchedIndexPattern } from '../../../../../common/types'; import { USE_KIBANA_INDEXES_KEY } from '../../../../../common/constants'; -import { IndexPattern } from '../../../../../../data/common'; +import { IndexPattern } from '../../../../../../../data/common'; export interface IndexPatternSelectProps { indexPatternName: string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts index 18288f75d4c9..244e95e8db9d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts @@ -7,7 +7,7 @@ */ import type { Assign } from '@kbn/utility-types'; import type { FetchedIndexPattern, IndexPatternValue } from '../../../../../common/types'; -import type { IndexPattern } from '../../../../../../data/common'; +import type { IndexPattern } from '../../../../../../../data/common'; /** @internal **/ export interface SelectIndexComponentProps { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts similarity index 89% rename from src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts index 28dd4c81510f..9c7cc1225c5a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts @@ -7,7 +7,7 @@ */ import uuid from 'uuid'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import type { Metric } from '../../../../common/types'; export const newMetricAggFn = (): Metric => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js b/src/plugins/vis_types/timeseries/public/application/components/lib/new_series_fn.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/new_series_fn.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.js b/src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/reorder.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/reorder.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/series_change_handler.js b/src/plugins/vis_types/timeseries/public/application/components/lib/series_change_handler.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/series_change_handler.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/series_change_handler.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/stacked.js b/src/plugins/vis_types/timeseries/public/application/components/lib/stacked.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/stacked.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/stacked.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js index 9b9beae67e44..8053c066114a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js @@ -7,10 +7,10 @@ */ import { createTickFormatter } from './tick_formatter'; -import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; +import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; import { setFieldFormats } from '../../../services'; -import { UI_SETTINGS } from '../../../../../data/public'; -import { FORMATS_UI_SETTINGS } from '../../../../../field_formats/common'; +import { UI_SETTINGS } from '../../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; const mockUiSettings = { get: (item) => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/types.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/types.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js rename to src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js index 046b1c579983..adee297fe011 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js +++ b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js @@ -15,7 +15,7 @@ import React, { Component } from 'react'; import { createTickFormatter } from './lib/tick_formatter'; import { convertSeriesToVars } from './lib/convert_series_to_vars'; import _ from 'lodash'; -import { CodeEditor, MarkdownLang } from '../../../../kibana_react/public'; +import { CodeEditor, MarkdownLang } from '../../../../../kibana_react/public'; import { EuiText, EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx index ae7fa5c59c73..81b33943f8b0 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import { PalettePicker, PalettePickerProps } from './palette_picker'; -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { EuiColorPalettePicker } from '@elastic/eui'; import { PALETTES } from '../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/palette_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/panel_config/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/_panel_config.scss b/src/plugins/vis_types/timeseries/public/application/components/panel_config/_panel_config.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/_panel_config.scss rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/_panel_config.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts b/src/plugins/vis_types/timeseries/public/application/components/panel_config/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx index 7f82f95d250e..b099209af434 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx @@ -42,7 +42,7 @@ import { getDefaultQueryLanguage } from '../lib/get_default_query_language'; import { VisDataContext } from '../../contexts/vis_data_context'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { TimeseriesVisParams } from '../../../types'; -import { CodeEditor, CssLang } from '../../../../../kibana_react/public'; +import { CodeEditor, CssLang } from '../../../../../../kibana_react/public'; const lessC = less(window, { env: 'production' }); diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/metric.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/metric.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/metric.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/metric.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/panel_config.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/panel_config.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/panel_config.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/panel_config.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx index 38cbd57b0b51..ecbd9767f34a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx @@ -42,7 +42,7 @@ import { BUCKET_TYPES } from '../../../../common/enums'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { TimeseriesVisParams } from '../../../types'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; export class TablePanelConfig extends Component< PanelConfigProps, diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/top_n.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/top_n.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/top_n.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/top_n.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/types.ts b/src/plugins/vis_types/timeseries/public/application/components/panel_config/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/types.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx b/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx rename to src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx index d3b249f54fe3..e0c66ea8d70a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx @@ -11,7 +11,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { CoreStartContext } from '../contexts/query_input_bar_context'; import type { IndexPatternValue } from '../../../common/types'; -import { QueryStringInput, QueryStringInputProps } from '../../../../../plugins/data/public'; +import { QueryStringInput, QueryStringInputProps } from '../../../../../../plugins/data/public'; import { getDataStart } from '../../services'; import { fetchIndexPattern, isStringTypeIndexPattern } from '../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/series.js b/src/plugins/vis_types/timeseries/public/application/components/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series.js rename to src/plugins/vis_types/timeseries/public/application/components/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config.js b/src/plugins/vis_types/timeseries/public/application/components/series_config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_config.js rename to src/plugins/vis_types/timeseries/public/application/components/series_config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js b/src/plugins/vis_types/timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js rename to src/plugins/vis_types/timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_drag_handler.tsx b/src/plugins/vis_types/timeseries/public/application/components/series_drag_handler.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_drag_handler.tsx rename to src/plugins/vis_types/timeseries/public/application/components/series_drag_handler.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_editor.js b/src/plugins/vis_types/timeseries/public/application/components/series_editor.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_editor.js rename to src/plugins/vis_types/timeseries/public/application/components/series_editor.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/split.js b/src/plugins/vis_types/timeseries/public/application/components/split.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/split.js rename to src/plugins/vis_types/timeseries/public/application/components/split.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap b/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/everything.js b/src/plugins/vis_types/timeseries/public/application/components/splits/everything.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/everything.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/everything.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filter.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filter.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filter_items.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filter_items.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filters.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filters.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filters.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filters.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js b/src/plugins/vis_types/timeseries/public/application/components/splits/group_by_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/group_by_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.js b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/splits/terms.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/terms.js index a668e5b727b4..b32af037533f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.js +++ b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js @@ -25,7 +25,7 @@ import { EuiFieldText, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; import { STACKED_OPTIONS } from '../../visualizations/constants'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/terms.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/unsupported_split.js b/src/plugins/vis_types/timeseries/public/application/components/splits/unsupported_split.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/unsupported_split.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/unsupported_split.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/svg/bomb_icon.js b/src/plugins/vis_types/timeseries/public/application/components/svg/bomb_icon.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/svg/bomb_icon.js rename to src/plugins/vis_types/timeseries/public/application/components/svg/bomb_icon.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/svg/fire_icon.js b/src/plugins/vis_types/timeseries/public/application/components/svg/fire_icon.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/svg/fire_icon.js rename to src/plugins/vis_types/timeseries/public/application/components/svg/fire_icon.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.scss b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.scss rename to src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx rename to src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx index b1722d409858..a73f9c6a5e09 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx @@ -27,7 +27,7 @@ import { LastValueModeIndicator } from './last_value_mode_indicator'; import { getInterval } from './lib/get_interval'; import { AUTO_INTERVAL } from '../../../common/constants'; import { TIME_RANGE_DATA_MODES, PANEL_TYPES } from '../../../common/enums'; -import type { IndexPattern } from '../../../../data/common'; +import type { IndexPattern } from '../../../../../data/common'; interface TimeseriesVisualizationProps { className?: string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/use_index_patter_mode_callout.tsx b/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/use_index_patter_mode_callout.tsx rename to src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx similarity index 95% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx index 5e4ff436ff1e..9e46427e33c2 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx @@ -15,11 +15,11 @@ import type { IUiSettingsClient } from 'kibana/public'; import type { Vis, VisualizeEmbeddableContract, -} from '../../../../../plugins/visualizations/public'; -import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; -import { Storage } from '../../../../../plugins/kibana_utils/public'; +} from '../../../../../../plugins/visualizations/public'; +import { KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; +import { Storage } from '../../../../../../plugins/kibana_utils/public'; -import type { TimeRange } from '../../../../../plugins/data/public'; +import type { TimeRange } from '../../../../../../plugins/data/public'; import type { IndexPatternValue, TimeseriesVisData } from '../../../common/types'; // @ts-expect-error @@ -32,7 +32,7 @@ import { fetchFields, VisFields } from '../lib/fetch_fields'; import { getDataStart, getCoreStart } from '../../services'; import type { TimeseriesVisParams } from '../../types'; import { UseIndexPatternModeCallout } from './use_index_patter_mode_callout'; -import type { EditorRenderProps } from '../../../../visualize/public'; +import type { EditorRenderProps } from '../../../../../visualize/public'; const VIS_STATE_DEBOUNCE_DELAY = 200; const APP_NAME = 'VisEditor'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_lazy.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor_lazy.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/_vis_types.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/_vis_types.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts similarity index 96% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts index b2e40940b800..653b2985ed1a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts @@ -14,7 +14,7 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { TimeseriesVisParams } from '../../../types'; import type { TimeseriesVisData, PanelData } from '../../../../common/types'; -import type { FieldFormatMap } from '../../../../../data/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; /** * Lazy load each visualization type, since the only one is presented on the screen at the same time. diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/_markdown.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/_markdown.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js index fc7019bd3829..49fdbcd98501 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js @@ -11,7 +11,7 @@ import React from 'react'; import classNames from 'classnames'; import uuid from 'uuid'; import { get } from 'lodash'; -import { Markdown } from '../../../../../../../plugins/kibana_react/public'; +import { Markdown } from '../../../../../../../../plugins/kibana_react/public'; import { ErrorComponent } from '../../error'; import { replaceVars } from '../../lib/replace_vars'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/is_sortable.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/is_sortable.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/is_sortable.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/is_sortable.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js index 21d7de9f1d88..7b1db4b36264 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js @@ -10,14 +10,14 @@ import _, { isArray, last, get } from 'lodash'; import React, { Component } from 'react'; import { parse as parseUrl } from 'url'; import PropTypes from 'prop-types'; -import { RedirectAppLinks } from '../../../../../../kibana_react/public'; +import { RedirectAppLinks } from '../../../../../../../kibana_react/public'; import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; import { createFieldFormatter } from '../../lib/create_field_formatter'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; -import { FIELD_FORMAT_IDS } from '../../../../../../../plugins/field_formats/common'; +import { FIELD_FORMAT_IDS } from '../../../../../../../../plugins/field_formats/common'; import { FormattedMessage } from '@kbn/i18n/react'; import { getFieldFormats, getCoreStart } from '../../../../services'; import { DATA_FORMATTERS } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index fed295fef9d3..75a8f11e640d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -18,7 +18,7 @@ import { createTickFormatter } from '../../lib/tick_formatter'; import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; -import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public'; +import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js similarity index 96% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js index d6e7484e903b..cf4c327df3d7 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js @@ -12,11 +12,11 @@ import { TimeSeries } from '../../../visualizations/views/timeseries'; import TimeseriesVisualization from './vis'; import { setFieldFormats } from '../../../../services'; import { createFieldFormatter } from '../../lib/create_field_formatter'; -import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; -import { METRIC_TYPES } from '../../../../../../data/common'; -import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; +import { FORMATS_UI_SETTINGS } from '../../../../../../../field_formats/common'; +import { METRIC_TYPES } from '../../../../../../../data/common'; +import { getFieldFormatsRegistry } from '../../../../../../../data/public/test_utils'; -jest.mock('../../../../../../data/public/services', () => ({ +jest.mock('../../../../../../../data/public/services', () => ({ getUiSettings: () => ({ get: jest.fn() }), })); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js b/src/plugins/vis_types/timeseries/public/application/components/vis_with_splits.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_with_splits.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/yes_no.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/yes_no.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/yes_no.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx b/src/plugins/vis_types/timeseries/public/application/components/yes_no.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx rename to src/plugins/vis_types/timeseries/public/application/components/yes_no.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/form_validation_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/form_validation_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/panel_model_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/panel_model_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/panel_model_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/panel_model_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/query_input_bar_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/query_input_bar_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/query_input_bar_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/query_input_bar_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/vis_data_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/vis_data_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/editor_controller.tsx b/src/plugins/vis_types/timeseries/public/application/editor_controller.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/editor_controller.tsx rename to src/plugins/vis_types/timeseries/public/application/editor_controller.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/index.scss b/src/plugins/vis_types/timeseries/public/application/index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/index.scss rename to src/plugins/vis_types/timeseries/public/application/index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/lib/check_ui_restrictions.js b/src/plugins/vis_types/timeseries/public/application/lib/check_ui_restrictions.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/check_ui_restrictions.js rename to src/plugins/vis_types/timeseries/public/application/lib/check_ui_restrictions.js diff --git a/src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.test.ts b/src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.test.ts rename to src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.ts b/src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.ts rename to src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts b/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts rename to src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts index 0a78e525796f..c447acb74dc3 100644 --- a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { getSplitByTermsColor, SplitByTermsColorProps } from './get_split_by_terms_color'; const chartsRegistry = chartPluginMock.createPaletteRegistry(); diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_timezone.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/get_timezone.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/index.ts b/src/plugins/vis_types/timeseries/public/application/lib/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/index.ts rename to src/plugins/vis_types/timeseries/public/application/lib/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/rainbow_colors.ts b/src/plugins/vis_types/timeseries/public/application/lib/rainbow_colors.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/rainbow_colors.ts rename to src/plugins/vis_types/timeseries/public/application/lib/rainbow_colors.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/set_is_reversed.js b/src/plugins/vis_types/timeseries/public/application/lib/set_is_reversed.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/set_is_reversed.js rename to src/plugins/vis_types/timeseries/public/application/lib/set_is_reversed.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/chart.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/chart.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/chart.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/chart.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/icons.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/icons.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/icons.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/icons.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/index.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/index.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/calc_dimensions.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/calc_dimensions.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/calc_dimensions.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/calc_dimensions.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/calculate_coordinates.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/calculate_coordinates.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/calculate_coordinates.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/calculate_coordinates.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/get_value_by.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/get_value_by.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/get_value_by.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/get_value_by.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_annotation.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_annotation.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_annotation.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_annotation.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_gauge.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_gauge.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_index.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_index.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_metric.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_metric.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_top_n.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_top_n.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/annotation.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/annotation.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/annotation.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/annotation.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge_vis.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge_vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge_vis.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge_vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/metric.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/metric.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 1edaf38ef403..6f6ddbbb7c41 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -32,7 +32,7 @@ import { getBaseTheme, getChartClasses } from './utils/theme'; import { TOOLTIP_MODES } from '../../../../../common/enums'; import { getValueOrEmpty } from '../../../../../common/empty_label'; import { getSplitByTermsColor } from '../../../lib/get_split_by_terms_color'; -import { renderEndzoneTooltip, useActiveCursor } from '../../../../../../charts/public'; +import { renderEndzoneTooltip, useActiveCursor } from '../../../../../../../charts/public'; import { getAxisLabelString } from '../../../components/lib/get_axis_label_string'; import { calculateDomainForSeries } from './utils/series_domain_calculation'; diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts index 5b502636003f..53157286328e 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts @@ -7,7 +7,7 @@ */ import { calculateDomainForSeries } from './series_domain_calculation'; -import { PanelData } from 'src/plugins/vis_type_timeseries/common/types'; +import { PanelData } from 'src/plugins/vis_types/timeseries/common/types'; describe('calculateDomainForSeries', () => { it('should return 0 for domainStart and 3 for domainEnd', () => { diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/top_n.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/top_n.js diff --git a/src/plugins/vis_type_timeseries/public/index.ts b/src/plugins/vis_types/timeseries/public/index.ts similarity index 88% rename from src/plugins/vis_type_timeseries/public/index.ts rename to src/plugins/vis_types/timeseries/public/index.ts index 0ab10581c48c..a3180678b53a 100644 --- a/src/plugins/vis_type_timeseries/public/index.ts +++ b/src/plugins/vis_types/timeseries/public/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PluginInitializerContext } from '../../../core/public'; +import { PluginInitializerContext } from '../../../../core/public'; import { MetricsPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/vis_type_timeseries/public/metrics_fn.ts b/src/plugins/vis_types/timeseries/public/metrics_fn.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/metrics_fn.ts rename to src/plugins/vis_types/timeseries/public/metrics_fn.ts index fe1c4722762a..23c196ebe114 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_fn.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_fn.ts @@ -7,8 +7,8 @@ */ import { i18n } from '@kbn/i18n'; -import { KibanaContext } from '../../data/public'; -import { ExpressionFunctionDefinition, Render } from '../../expressions/public'; +import { KibanaContext } from '../../../data/public'; +import { ExpressionFunctionDefinition, Render } from '../../../expressions/public'; import type { TimeseriesVisData } from '../common/types'; import { metricsRequestHandler } from './request_handler'; diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/metrics_type.ts rename to src/plugins/vis_types/timeseries/public/metrics_type.ts index 5d4a61c1edb8..64970d9730ee 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -19,7 +19,7 @@ import { VisGroups, VisParams, VisTypeDefinition, -} from '../../visualizations/public'; +} from '../../../visualizations/public'; import { getDataStart } from './services'; import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types'; diff --git a/src/plugins/vis_type_timeseries/public/plugin.ts b/src/plugins/vis_types/timeseries/public/plugin.ts similarity index 86% rename from src/plugins/vis_type_timeseries/public/plugin.ts rename to src/plugins/vis_types/timeseries/public/plugin.ts index 3cd090c7da82..d6d83caa6eb0 100644 --- a/src/plugins/vis_type_timeseries/public/plugin.ts +++ b/src/plugins/vis_types/timeseries/public/plugin.ts @@ -7,9 +7,9 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { VisualizePluginSetup } from '../../visualize/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { VisualizationsSetup } from '../../../visualizations/public'; +import { VisualizePluginSetup } from '../../../visualize/public'; import { EditorController, TSVB_EDITOR_NAME } from './application/editor_controller'; import { createMetricsFn } from './metrics_fn'; @@ -22,8 +22,8 @@ import { setDataStart, setCharts, } from './services'; -import { DataPublicPluginStart } from '../../data/public'; -import { ChartsPluginStart } from '../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { ChartsPluginStart } from '../../../charts/public'; import { getTimeseriesVisRenderer } from './timeseries_vis_renderer'; /** @internal */ diff --git a/src/plugins/vis_type_timeseries/public/request_handler.ts b/src/plugins/vis_types/timeseries/public/request_handler.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/request_handler.ts rename to src/plugins/vis_types/timeseries/public/request_handler.ts index 0a110dd65d5e..e9037c0b84a5 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.ts +++ b/src/plugins/vis_types/timeseries/public/request_handler.ts @@ -12,7 +12,7 @@ import { ROUTES } from '../common/constants'; import type { TimeseriesVisParams } from './types'; import type { TimeseriesVisData } from '../common/types'; -import type { KibanaContext } from '../../data/public'; +import type { KibanaContext } from '../../../data/public'; interface MetricsRequestHandlerParams { input: KibanaContext | null; diff --git a/src/plugins/vis_type_timeseries/public/services.ts b/src/plugins/vis_types/timeseries/public/services.ts similarity index 84% rename from src/plugins/vis_type_timeseries/public/services.ts rename to src/plugins/vis_types/timeseries/public/services.ts index ba7ab4a25c8a..f76a9ed7c638 100644 --- a/src/plugins/vis_type_timeseries/public/services.ts +++ b/src/plugins/vis_types/timeseries/public/services.ts @@ -7,9 +7,9 @@ */ import { I18nStart, IUiSettingsClient, CoreStart } from 'src/core/public'; -import { createGetterSetter } from '../../kibana_utils/public'; -import { ChartsPluginStart } from '../../charts/public'; -import { DataPublicPluginStart } from '../../data/public'; +import { createGetterSetter } from '../../../kibana_utils/public'; +import { ChartsPluginStart } from '../../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); diff --git a/src/plugins/vis_type_timeseries/public/test_utils/index.ts b/src/plugins/vis_types/timeseries/public/test_utils/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/test_utils/index.ts rename to src/plugins/vis_types/timeseries/public/test_utils/index.ts diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx similarity index 94% rename from src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx rename to src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx index 9a19ddc285eb..34cc1dc347ef 100644 --- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx @@ -14,14 +14,14 @@ import { I18nProvider } from '@kbn/i18n/react'; import { IUiSettingsClient } from 'kibana/public'; import { fetchIndexPattern } from '../common/index_patterns_utils'; -import { VisualizationContainer, PersistedState } from '../../visualizations/public'; +import { VisualizationContainer, PersistedState } from '../../../visualizations/public'; import type { TimeseriesVisData } from '../common/types'; import { isVisTableData } from '../common/vis_data_utils'; import { getCharts, getDataStart } from './services'; import type { TimeseriesVisParams } from './types'; -import type { ExpressionRenderDefinition } from '../../expressions/common'; +import type { ExpressionRenderDefinition } from '../../../expressions/common'; import type { TimeseriesRenderValue } from './metrics_fn'; const TimeseriesVisualization = lazy( diff --git a/src/plugins/vis_type_timeseries/public/to_ast.ts b/src/plugins/vis_types/timeseries/public/to_ast.ts similarity index 91% rename from src/plugins/vis_type_timeseries/public/to_ast.ts rename to src/plugins/vis_types/timeseries/public/to_ast.ts index c0c0a5b1546a..91fc7465f122 100644 --- a/src/plugins/vis_type_timeseries/public/to_ast.ts +++ b/src/plugins/vis_types/timeseries/public/to_ast.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import type { Vis } from '../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; +import type { Vis } from '../../../visualizations/public'; import type { TimeseriesExpressionFunctionDefinition } from './metrics_fn'; import type { TimeseriesVisParams } from './types'; diff --git a/src/plugins/vis_type_timeseries/public/types.ts b/src/plugins/vis_types/timeseries/public/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/types.ts rename to src/plugins/vis_types/timeseries/public/types.ts diff --git a/src/plugins/vis_type_timeseries/server/config.ts b/src/plugins/vis_types/timeseries/server/config.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/config.ts rename to src/plugins/vis_types/timeseries/server/config.ts diff --git a/src/plugins/vis_type_timeseries/server/index.ts b/src/plugins/vis_types/timeseries/server/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/index.ts rename to src/plugins/vis_types/timeseries/server/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts b/src/plugins/vis_types/timeseries/server/lib/get_fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/get_fields.ts rename to src/plugins/vis_types/timeseries/server/lib/get_fields.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts rename to src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/index.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/index.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts index 5f989a50ca63..2a3738878c97 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts @@ -8,7 +8,7 @@ import { getIndexPatternKey, fetchIndexPattern } from '../../../../common/index_patterns_utils'; -import type { IndexPatternsService } from '../../../../../data/server'; +import type { IndexPatternsService } from '../../../../../../data/server'; import type { IndexPatternValue, FetchedIndexPattern } from '../../../../common/types'; export const getCachedIndexPatternFetcher = ( diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts index 9563a8fbece2..cf7bc42fc6db 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts @@ -10,7 +10,7 @@ import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; import type { VisTypeTimeseriesVisDataRequest } from '../../../types'; import type { SearchStrategy, SearchCapabilities } from '../index'; -import type { IndexPatternsService } from '../../../../../data/common'; +import type { IndexPatternsService } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from './cached_index_pattern_fetcher'; import type { IndexPatternValue } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategies_registry.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategies_registry.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategies_registry.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategies_registry.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategy_registry.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategy_registry.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategy_registry.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategy_registry.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts index 0a9b3d0047c8..6216bce00fc7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { IndexPatternsService } from '../../../../../data/common'; +import { IndexPatternsService } from '../../../../../../data/common'; import { from } from 'rxjs'; import { AbstractSearchStrategy } from './abstract_search_strategy'; -import type { FieldSpec } from '../../../../../data/common'; +import type { FieldSpec } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from '../lib/cached_index_pattern_fetcher'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 26c3a6c7c8bf..bce07d2cdb30 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexPatternsService } from '../../../../../data/server'; +import { IndexPatternsService } from '../../../../../../data/server'; import { toSanitizedFieldType } from '../../../../common/fields_utils'; import type { FetchedIndexPattern } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts index 34892ec797c0..0fa92b5f061f 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts @@ -9,7 +9,7 @@ import { AbstractSearchStrategy } from './abstract_search_strategy'; import { DefaultSearchCapabilities } from '../capabilities/default_search_capabilities'; -import type { IndexPatternsService } from '../../../../../data/server'; +import type { IndexPatternsService } from '../../../../../../data/server'; import type { FetchedIndexPattern } from '../../../../common/types'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/index.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/index.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts index 85b8bcdf57b9..4d2608e3519e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts @@ -8,7 +8,7 @@ import { RollupSearchStrategy } from './rollup_search_strategy'; -import type { IndexPatternsService } from '../../../../../data/common'; +import type { IndexPatternsService } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from '../lib/cached_index_pattern_fetcher'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts index f68c877cf7a3..903e7f239f82 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { getCapabilitiesForRollupIndices, IndexPatternsService } from '../../../../../data/server'; +import { + getCapabilitiesForRollupIndices, + IndexPatternsService, +} from '../../../../../../data/server'; import { AbstractSearchStrategy } from './abstract_search_strategy'; import { RollupSearchCapabilities } from '../capabilities/rollup_search_capabilities'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/get_request_params.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/get_request_params.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_annotations.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_annotations.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_annotations.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_annotations.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_series_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_series_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_table_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_table_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/check_aggs.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/check_aggs.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/check_aggs.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/check_aggs.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/format_key.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/format_key.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/format_key.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/format_key.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_active_series.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_active_series.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_active_series.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_active_series.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js index 6756a8f7fc85..eac2c4af6529 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js @@ -8,7 +8,7 @@ import { get, max, min, sum, noop } from 'lodash'; import { toPercentileNumber } from '../../../../common/to_percentile_number'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import { getAggByPredicate } from '../../../../common/agg_utils'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts similarity index 98% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts index 7f5874c0763f..e02b403c8ba1 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts @@ -15,7 +15,7 @@ import { } from './unit_to_seconds'; import { getTimerange } from './get_timerange'; import { INTERVAL_STRING_RE, GTE_INTERVAL_RE } from '../../../../common/interval_regexp'; -import { search } from '../../../../../data/server'; +import { search } from '../../../../../../data/server'; import type { SearchCapabilities } from '../../search_strategies'; import type { VisTypeTimeseriesVisDataRequest } from '../../../types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts index be8755584e2c..06df19ab7385 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts @@ -8,7 +8,7 @@ import { startsWith } from 'lodash'; import { toPercentileNumber } from '../../../../common/to_percentile_number'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import type { Metric } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts similarity index 93% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts index c5ceac5cd7dd..07b030782d6d 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts @@ -7,7 +7,7 @@ */ import { IUiSettingsClient } from 'kibana/server'; -import { UI_SETTINGS } from '../../../../../data/server'; +import { UI_SETTINGS } from '../../../../../../data/server'; export async function getEsQueryConfig(uiSettings: IUiSettingsClient) { const allowLeadingWildcards = await uiSettings.get(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts index d52b6b38a7bd..fd061cd36369 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts @@ -7,7 +7,7 @@ */ import { mapEmptyToZero } from './map_empty_to_zero'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; describe('mapEmptyToZero(metric, buckets)', () => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts index a035d566d130..26014b229bae 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts @@ -8,7 +8,7 @@ // @ts-expect-error not typed yet import { getAggValue } from './get_agg_value'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import type { Metric } from '../../../../common/types'; import type { PanelDataArray } from '../../../../common/types/vis_data'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/overwrite.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/overwrite.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/overwrite.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/overwrite.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_interval.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_interval.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts index 258dc2d54137..a52e15eb90fe 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts @@ -10,7 +10,7 @@ import { overwrite } from '../../helpers'; import { getBucketSize, getTimerange } from '../../helpers'; import { validateField } from '../../../../../common/fields_utils'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import type { AnnotationsRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts index 53fe51329acb..f1b11a780fec 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts @@ -8,7 +8,7 @@ import { getBucketSize, getTimerange, overwrite } from '../../helpers'; import { validateField } from '../../../../../common/fields_utils'; -import { esQuery, UI_SETTINGS } from '../../../../../../data/server'; +import { esQuery, UI_SETTINGS } from '../../../../../../../data/server'; import type { AnnotationsRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js index 6349a75993aa..696bea7d6421 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js @@ -10,7 +10,7 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { offsetTime } from '../../offset_time'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import { AGG_TYPE, getAggsByType } from '../../../../../common/agg_utils'; import { TSVB_METRIC_TYPES } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js similarity index 99% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index b09b2c28d77e..6a7f09a49f26 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -9,7 +9,7 @@ import { DefaultSearchCapabilities } from '../../../search_strategies/capabilities/default_search_capabilities'; import { dateHistogram } from './date_histogram'; import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; describe('dateHistogram(req, panel, series)', () => { let panel; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js index d45943f6f21a..32e9a1ea0aa9 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js @@ -8,7 +8,7 @@ import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; const filter = (metric) => metric.type === 'filter_ratio'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/index.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/index.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/index.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js index 91016384794c..86c5311f51dc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js @@ -9,7 +9,7 @@ import { getBucketSize } from '../../helpers/get_bucket_size'; import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; export const filter = (metric) => metric.type === 'positive_rate'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js index 5031a0f2ec18..f745a7bf7ad7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js @@ -7,7 +7,7 @@ */ import { offsetTime } from '../../offset_time'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function query( req, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js similarity index 92% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js index 01e1b9f8d1dc..b7e7754d05f8 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function splitByFilter(req, panel, series, esQueryConfig, seriesIndex) { return (next) => (doc) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js similarity index 93% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js index 77b9ccc5880f..5e8def5db771 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function splitByFilters(req, panel, series, esQueryConfig, seriesIndex) { return (next) => (doc) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts index 5dee812dd4c5..c02f661c3aed 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts @@ -8,7 +8,7 @@ import { overwrite, getBucketSize, isLastValueTimerangeMode, getTimerange } from '../../helpers'; import { calculateAggRoot } from './calculate_agg_root'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import { AGG_TYPE, getAggsByType } from '../../../../../common/agg_utils'; import { TSVB_METRIC_TYPES } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts index e8fb684ef4b6..b6036a03ba46 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import { overwrite, bucketTransform } from '../../helpers'; import { calculateAggRoot } from './calculate_agg_root'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/pivot.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/pivot.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/pivot.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/pivot.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts index 85a817e124aa..e03a65ffebbd 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts @@ -8,7 +8,7 @@ import { getBucketSize } from '../../helpers/get_bucket_size'; import { calculateAggRoot } from './calculate_agg_root'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts index 5ddf83e1134b..76c4649ee573 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts @@ -7,7 +7,7 @@ */ import { getTimerange, overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; export const query: TableRequestProcessorsFunction = diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts index cca86a1960fc..e0e1485f6a8f 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts index 35b0ba50a05b..1a4e0f6ceb1b 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts index d47d3fe34c9b..6bb4dfa55426 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts @@ -8,7 +8,7 @@ import type { IUiSettingsClient } from 'kibana/server'; import type { FetchedIndexPattern, Panel } from '../../../../../common/types'; -import type { EsQueryConfig } from '../../../../../../data/common'; +import type { EsQueryConfig } from '../../../../../../../data/common'; import type { SearchCapabilities } from '../../../search_strategies'; import type { VisTypeTimeseriesVisDataRequest } from '../../../../types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts index 7908cbccb984..6d824c1c7f43 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts @@ -9,7 +9,7 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { BUCKET_TYPES, PANEL_TYPES } from '../../../../../common/enums'; import type { Panel, PanelData, Series } from '../../../../../common/types'; -import type { FieldFormatsRegistry } from '../../../../../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../../../../../field_formats/common'; import type { createFieldsFetcher } from '../../../search_strategies/lib/fields_fetcher'; import type { CachedIndexPatternFetcher } from '../../../search_strategies/lib/cached_index_pattern_fetcher'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/index.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/_series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/_series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/_series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/_series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/math.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/math.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/math.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/math.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/series_agg.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/series_agg.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/series_agg.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/series_agg.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_metric.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_metric.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_metric.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_metric.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/get_request_params.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/get_request_params.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts index 78e9f971a61d..415844abeeda 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts @@ -17,7 +17,7 @@ import { FieldsFetcherServices, } from '../../search_strategies/lib/fields_fetcher'; import { VisTypeTimeseriesVisDataRequest } from '../../../types'; -import type { FieldFormatsRegistry } from '../../../../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../../../../field_formats/common'; export function handleResponseBody( panel: Panel, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_response_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_response_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_response_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_response_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.ts diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_types/timeseries/server/plugin.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/plugin.ts rename to src/plugins/vis_types/timeseries/server/plugin.ts index d2ecb07c0273..5347d9ab7bfb 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_types/timeseries/server/plugin.ts @@ -20,9 +20,9 @@ import { Server } from '@hapi/hapi'; import { first, map } from 'rxjs/operators'; import { VisTypeTimeseriesConfig } from './config'; import { getVisData } from './lib/get_vis_data'; -import { UsageCollectionSetup } from '../../usage_collection/server'; -import { PluginStart } from '../../data/server'; -import { IndexPatternsService } from '../../data/common'; +import { UsageCollectionSetup } from '../../../usage_collection/server'; +import { PluginStart } from '../../../data/server'; +import { IndexPatternsService } from '../../../data/common'; import { visDataRoutes } from './routes/vis'; import { fieldsRoutes } from './routes/fields'; import { getUiSettings } from './ui_settings'; @@ -30,7 +30,7 @@ import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesVisDataRequest, } from './types'; -import type { FieldFormatsRegistry } from '../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../field_formats/common'; import { SearchStrategyRegistry, diff --git a/src/plugins/vis_type_timeseries/server/routes/fields.ts b/src/plugins/vis_types/timeseries/server/routes/fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/routes/fields.ts rename to src/plugins/vis_types/timeseries/server/routes/fields.ts diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_types/timeseries/server/routes/vis.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/routes/vis.ts rename to src/plugins/vis_types/timeseries/server/routes/vis.ts diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_types/timeseries/server/types.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/types.ts rename to src/plugins/vis_types/timeseries/server/types.ts index 40ced7293301..ab01f09c75f1 100644 --- a/src/plugins/vis_type_timeseries/server/types.ts +++ b/src/plugins/vis_types/timeseries/server/types.ts @@ -10,8 +10,8 @@ import { Observable } from 'rxjs'; import { EsQueryConfig } from '@kbn/es-query'; import { SharedGlobalConfig } from 'kibana/server'; import type { IRouter, IUiSettingsClient, KibanaRequest } from 'src/core/server'; -import type { DataRequestHandlerContext, IndexPatternsService } from '../../data/server'; -import type { FieldFormatsRegistry } from '../../field_formats/common'; +import type { DataRequestHandlerContext, IndexPatternsService } from '../../../data/server'; +import type { FieldFormatsRegistry } from '../../../field_formats/common'; import type { Series, VisPayload } from '../common/types'; import type { SearchStrategyRegistry } from './lib/search_strategies'; import type { CachedIndexPatternFetcher } from './lib/search_strategies/lib/cached_index_pattern_fetcher'; diff --git a/src/plugins/vis_type_timeseries/server/ui_settings.ts b/src/plugins/vis_types/timeseries/server/ui_settings.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/ui_settings.ts rename to src/plugins/vis_types/timeseries/server/ui_settings.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.mock.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.mock.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts index 91daf09121f1..aac6d879f48f 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts @@ -7,8 +7,11 @@ */ import { getStats } from './get_usage_collector'; -import { createCollectorFetchContextMock } from '../../../usage_collection/server/mocks'; -import type { SavedObjectsClientContract, SavedObjectsFindResponse } from '../../../../core/server'; +import { createCollectorFetchContextMock } from '../../../../usage_collection/server/mocks'; +import type { + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from '../../../../../core/server'; import { TIME_RANGE_DATA_MODES } from '../../common/enums'; const mockedSavedObject = { diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts similarity index 93% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts index a8d64afaf8ce..8309d51a9d56 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts @@ -7,14 +7,14 @@ */ import { TIME_RANGE_DATA_MODES } from '../../common/enums'; -import { findByValueEmbeddables } from '../../../dashboard/server'; +import { findByValueEmbeddables } from '../../../../dashboard/server'; import type { SavedObjectsClientContract, ISavedObjectsRepository, SavedObjectsFindResult, -} from '../../../../core/server'; -import type { SavedVisState } from '../../../visualizations/common'; +} from '../../../../../core/server'; +import type { SavedVisState } from '../../../../visualizations/common'; export interface TimeseriesUsage { timeseries_use_last_value_mode_total: number; diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/index.ts b/src/plugins/vis_types/timeseries/server/usage_collector/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/usage_collector/index.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/index.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts similarity index 92% rename from src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts index 0dfe5ae5f935..26a74821fe5a 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts @@ -7,8 +7,8 @@ */ import { mockStats, mockGetStats } from './get_usage_collector.mock'; -import { createUsageCollectionSetupMock } from '../../../usage_collection/server/mocks'; -import { createCollectorFetchContextMock } from '../../../usage_collection/server/mocks'; +import { createUsageCollectionSetupMock } from '../../../../usage_collection/server/mocks'; +import { createCollectorFetchContextMock } from '../../../../usage_collection/server/mocks'; import { registerTimeseriesUsageCollector } from './register_timeseries_collector'; describe('registerTimeseriesUsageCollector', () => { diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts similarity index 92% rename from src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts index 7e9294f03ba1..6fccd7ef3017 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts @@ -7,7 +7,7 @@ */ import { getStats, TimeseriesUsage } from './get_usage_collector'; -import type { UsageCollectionSetup } from '../../../usage_collection/server'; +import type { UsageCollectionSetup } from '../../../../usage_collection/server'; export function registerTimeseriesUsageCollector(collectorSet: UsageCollectionSetup) { const collector = collectorSet.makeUsageCollector({ diff --git a/src/plugins/vis_types/timeseries/tsconfig.json b/src/plugins/vis_types/timeseries/tsconfig.json new file mode 100644 index 000000000000..f336a467e0c8 --- /dev/null +++ b/src/plugins/vis_types/timeseries/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "*.ts" + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../visualize/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../kibana_react/tsconfig.json" }, + { "path": "../../usage_collection/tsconfig.json" }, + ] +} diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 3cd435ab0f6e..4d4a0ff6320b 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -15,7 +15,7 @@ import { PluginStart as DataPluginStart, } from '../../../../../../../src/plugins/data/server'; import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server'; -import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_types/timeseries/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../plugins/features/server'; import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerting/server'; diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 2aede2f6aad1..4576a2e8452a 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -34,7 +34,7 @@ import { RequestHandler } from '../../../../../../../src/core/server'; import { InfraConfig } from '../../../plugin'; import type { InfraPluginRequestHandlerContext } from '../../../types'; import { UI_SETTINGS } from '../../../../../../../src/plugins/data/server'; -import { TimeseriesVisData } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { TimeseriesVisData } from '../../../../../../../src/plugins/vis_types/timeseries/server'; import { InfraServerPluginStartDeps } from './adapter_types'; export class KibanaFramework { diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index a9ddcc8d3d4c..730da9511dc3 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -21,7 +21,7 @@ import { import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; import { CallWithRequestParams, InfraDatabaseSearchResponse } from '../framework'; import type { InfraPluginRequestHandlerContext } from '../../../types'; -import { isVisSeriesData } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { isVisSeriesData } from '../../../../../../../src/plugins/vis_types/timeseries/server'; export class KibanaMetricsAdapter implements InfraMetricsAdapter { private framework: KibanaFramework; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index a9739bdfdedc..a2d1d2b63655 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -22,7 +22,7 @@ { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/vis_type_timeseries/tsconfig.json" }, + { "path": "../../../src/plugins/vis_types/timeseries/tsconfig.json" }, { "path": "../data_enhanced/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../features/tsconfig.json" }, diff --git a/x-pack/plugins/rollup/server/types.ts b/x-pack/plugins/rollup/server/types.ts index c774644da46c..a3e826fefa0b 100644 --- a/x-pack/plugins/rollup/server/types.ts +++ b/x-pack/plugins/rollup/server/types.ts @@ -7,7 +7,7 @@ import { IRouter } from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; +import { VisTypeTimeseriesSetup } from 'src/plugins/vis_types/timeseries/server'; import { getCapabilitiesForRollupIndices } from 'src/plugins/data/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; diff --git a/x-pack/plugins/rollup/tsconfig.json b/x-pack/plugins/rollup/tsconfig.json index fbe323b2549e..252c27a66fba 100644 --- a/x-pack/plugins/rollup/tsconfig.json +++ b/x-pack/plugins/rollup/tsconfig.json @@ -22,7 +22,7 @@ { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../index_management/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/vis_type_timeseries/tsconfig.json" }, + { "path": "../../../src/plugins/vis_types/timeseries/tsconfig.json" }, // required bundles { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, From dd56ac3bcdcacc9951c0e455cf9b894634ffd525 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 07:05:01 -0400 Subject: [PATCH 16/36] Update dependency @types/node-forge to ^0.10.5 (master) (#112625) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f3af0ba80cfc..c24f61e55655 100644 --- a/package.json +++ b/package.json @@ -573,7 +573,7 @@ "@types/nock": "^10.0.3", "@types/node": "14.14.44", "@types/node-fetch": "^2.5.7", - "@types/node-forge": "^0.10.4", + "@types/node-forge": "^0.10.5", "@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 ef179cc8105e..a69450d4b628 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5631,10 +5631,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node-forge@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.4.tgz#f30025cc2da0177393b9deaefbf3b9edd55b807b" - integrity sha512-RpP7JCxlPA32n8FE0kjOpCsCrsX6VjiD0fjOCo4NwIn8IdcicHi4B2e+votWuOpOmwzUjMwRLqVIF95epGd5nA== +"@types/node-forge@^0.10.5": + version "0.10.5" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.5.tgz#f79925c88202817a7ec0958c3a9d3a915d362b4f" + integrity sha512-P+Q+MPSDr0RgIzv5h0gJuJDCm1e4RaSu/EMJZTUS4ZzboWH2uX/T7TiqAAcEFTHzCKtgMRqCgTVTX9SD72fMTQ== dependencies: "@types/node" "*" From 1ecb42db2ac5067b2470a0939907546862aa5f8f Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 21 Sep 2021 14:05:55 +0300 Subject: [PATCH 17/36] [i18n] fix type (#112642) --- .../document_fields/field_parameters/path_parameter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx index aeb4debddcd6..7307dd0e41ed 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx @@ -86,7 +86,7 @@ export const PathParameter = ({ field, allFields }: Props) => { 'xpack.idxMgmt.mappingsEditor.aliasType.aliasTargetFieldDescription', { defaultMessage: - 'Select the field you want your alias to point to. You will then be able to use the alias instead of the target field in search requests, and selected other APIs like field capabilities.', + 'Select the field you want your alias to point to. You will then be able to use the alias instead of the target field in search requests and select other APIs like field capabilities.', } )} withToggle={false} From b2bc5a592ddd58daa435bc6725cfdfbe1c81ef0a Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 21 Sep 2021 06:10:00 -0500 Subject: [PATCH 18/36] [storybook] Fix Shared UI Dep paths (#112631) --- packages/kbn-storybook/templates/index.ejs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/kbn-storybook/templates/index.ejs b/packages/kbn-storybook/templates/index.ejs index 1fca20b1ff8e..3473f73ca131 100644 --- a/packages/kbn-storybook/templates/index.ejs +++ b/packages/kbn-storybook/templates/index.ejs @@ -16,12 +16,11 @@ - - + - + From 322c5e26f09e74aa129ae8fd5f25842ae86767d3 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 21 Sep 2021 13:15:05 +0200 Subject: [PATCH 19/36] [Uptime] Update no data available state (#112403) * wip * update component * update paths * fix i18n * fix tests * revert uneeded --- .../translations/translations/ja-JP.json | 6 -- .../translations/translations/zh-CN.json | 6 -- .../public/apps/uptime_page_template.tsx | 64 ++++++++++++ .../uptime/public/apps/use_no_data_config.ts | 46 +++++++++ .../data_or_index_missing.test.tsx | 26 ----- .../empty_state/data_or_index_missing.tsx | 87 ---------------- .../overview/empty_state/empty_state.test.tsx | 99 ------------------- .../overview/empty_state/empty_state.tsx | 73 -------------- .../empty_state/empty_state_container.tsx | 54 ---------- .../components/overview/empty_state/index.ts | 9 -- .../overview/empty_state/use_has_data.tsx | 36 +++++++ .../public/components/overview/index.ts | 1 - .../plugins/uptime/public/pages/overview.tsx | 5 +- x-pack/plugins/uptime/public/routes.tsx | 19 +--- 14 files changed, 151 insertions(+), 380 deletions(-) create mode 100644 x-pack/plugins/uptime/public/apps/uptime_page_template.tsx create mode 100644 x-pack/plugins/uptime/public/apps/use_no_data_config.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/index.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f49d2d674330..9c69e4fa612f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26137,13 +26137,7 @@ "xpack.uptime.createPackagePolicy.stepConfigure.tlsSettings.label": "TLS設定", "xpack.uptime.durationChart.emptyPrompt.description": "このモニターは選択された時間範囲で一度も{emphasizedText}していません。", "xpack.uptime.durationChart.emptyPrompt.title": "利用可能な期間データがありません", - "xpack.uptime.emptyState.configureHeartbeatIndexSettings": "Heartbeatがすでに設定されている場合は、データがElasticsearchに送信されていることを確認してから、Heartbeat構成に合わせてインデックスパターン設定を更新します。", - "xpack.uptime.emptyState.configureHeartbeatToGetStartedMessage": "サービスの監視を開始するには、Heartbeatを設定します。", "xpack.uptime.emptyState.loadingMessage": "読み込み中…", - "xpack.uptime.emptyState.noDataMessage": "インデックス{indexName}にはアップタイムデータが見つかりません", - "xpack.uptime.emptyState.noIndexTitle": "パターン{indexName}のインデックスが見つかりません", - "xpack.uptime.emptyState.updateIndexPattern": "インデックスパターン設定を更新", - "xpack.uptime.emptyState.viewSetupInstructions": "セットアップの手順を表示", "xpack.uptime.emptyStateError.notAuthorized": "アップタイムデータの表示が承認されていません。システム管理者にお問い合わせください。", "xpack.uptime.emptyStateError.notFoundPage": "ページが見つかりません", "xpack.uptime.emptyStateError.title": "エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 85e11905e5eb..1c91700a74e8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -26572,13 +26572,7 @@ "xpack.uptime.createPackagePolicy.stepConfigure.tlsSettings.label": "TLS 设置", "xpack.uptime.durationChart.emptyPrompt.description": "在选定时间范围内此监测从未{emphasizedText}。", "xpack.uptime.durationChart.emptyPrompt.title": "没有持续时间数据", - "xpack.uptime.emptyState.configureHeartbeatIndexSettings": "如果已设置 Heartbeat,请确认其正向 Elasticsearch 发送数据,然后更新索引模式设置以匹配 Heartbeat 配置。", - "xpack.uptime.emptyState.configureHeartbeatToGetStartedMessage": "设置 Heartbeat 以开始监测您的服务。", "xpack.uptime.emptyState.loadingMessage": "正在加载……", - "xpack.uptime.emptyState.noDataMessage": "在索引 {indexName} 中找不到运行时间数据", - "xpack.uptime.emptyState.noIndexTitle": "找不到模式 {indexName} 的索引", - "xpack.uptime.emptyState.updateIndexPattern": "更新索引模式设置", - "xpack.uptime.emptyState.viewSetupInstructions": "查看设置说明", "xpack.uptime.emptyStateError.notAuthorized": "您无权查看 Uptime 数据,请联系系统管理员。", "xpack.uptime.emptyStateError.notFoundPage": "未找到页面", "xpack.uptime.emptyStateError.title": "错误", diff --git a/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx b/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx new file mode 100644 index 000000000000..829f587e248e --- /dev/null +++ b/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import styled from 'styled-components'; +import { EuiPageHeaderProps } from '@elastic/eui'; +import { OVERVIEW_ROUTE } from '../../common/constants'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { ClientPluginsStart } from './plugin'; +import { useNoDataConfig } from './use_no_data_config'; +import { EmptyStateLoading } from '../components/overview/empty_state/empty_state_loading'; +import { EmptyStateError } from '../components/overview/empty_state/empty_state_error'; +import { useHasData } from '../components/overview/empty_state/use_has_data'; + +interface Props { + path: string; + pageHeader?: EuiPageHeaderProps; +} + +export const UptimePageTemplateComponent: React.FC = ({ path, pageHeader, children }) => { + const { + services: { observability }, + } = useKibana(); + + const PageTemplateComponent = observability.navigation.PageTemplate; + + const StyledPageTemplateComponent = useMemo(() => { + return styled(PageTemplateComponent)` + .euiPageHeaderContent > .euiFlexGroup { + flex-wrap: wrap; + } + `; + }, [PageTemplateComponent]); + + const noDataConfig = useNoDataConfig(); + + const { loading, error } = useHasData(); + + if (error) { + return ; + } + + return ( + <> +
+ + {loading && path === OVERVIEW_ROUTE && } +
+ {children} +
+
+ + ); +}; diff --git a/x-pack/plugins/uptime/public/apps/use_no_data_config.ts b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts new file mode 100644 index 000000000000..dc00a25e3a11 --- /dev/null +++ b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useContext } from 'react'; +import { useSelector } from 'react-redux'; +import { KibanaPageTemplateProps, useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { UptimeSettingsContext } from '../contexts'; +import { ClientPluginsStart } from './plugin'; +import { indexStatusSelector } from '../state/selectors'; + +export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { + const { basePath } = useContext(UptimeSettingsContext); + + const { + services: { docLinks }, + } = useKibana(); + + const { data } = useSelector(indexStatusSelector); + + // Returns no data config when there is no historical data + if (data && !data.indexExists) { + return { + solution: i18n.translate('xpack.uptime.noDataConfig.solutionName', { + defaultMessage: 'Observability', + }), + actions: { + beats: { + title: i18n.translate('xpack.uptime.noDataConfig.beatsCard.title', { + defaultMessage: 'Add monitors with Heartbeat', + }), + description: i18n.translate('xpack.uptime.noDataConfig.beatsCard.description', { + defaultMessage: + 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', + }), + href: basePath + `/app/home#/tutorial/uptimeMonitors`, + }, + }, + docsLink: docLinks!.links.observability.guide, + }; + } +} diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx deleted file mode 100644 index caff055ce987..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx +++ /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 React from 'react'; -import { screen } from '@testing-library/react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { render } from '../../../lib/helper/rtl_helpers'; -import { DataOrIndexMissing } from './data_or_index_missing'; - -describe('DataOrIndexMissing component', () => { - it('renders headingMessage', () => { - const headingMessage = ( - heartbeat-* }} - /> - ); - render(); - expect(screen.getByText(/heartbeat-*/)).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx deleted file mode 100644 index 44e55de990bb..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiFlexGroup, - EuiEmptyPrompt, - EuiFlexItem, - EuiSpacer, - EuiPanel, - EuiTitle, - EuiButton, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useContext } from 'react'; -import { UptimeSettingsContext } from '../../../contexts'; -import { DynamicSettings } from '../../../../common/runtime_types'; - -interface DataMissingProps { - headingMessage: JSX.Element; - settings?: DynamicSettings; -} - -export const DataOrIndexMissing = ({ headingMessage, settings }: DataMissingProps) => { - const { basePath } = useContext(UptimeSettingsContext); - return ( - - - - - -

{headingMessage}

- - } - body={ - <> -

- -

-

- -

- - } - actions={ - - - - - - - - - - - - - } - /> -
-
-
- ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx deleted file mode 100644 index 45b107928d79..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { EmptyStateComponent } from './empty_state'; -import { StatesIndexStatus } from '../../../../common/runtime_types'; -import { HttpFetchError, IHttpFetchError } from 'src/core/public'; -import { render } from '../../../lib/helper/rtl_helpers'; - -describe('EmptyState component', () => { - let statesIndexStatus: StatesIndexStatus; - - beforeEach(() => { - statesIndexStatus = { - indexExists: true, - docCount: 1, - indices: 'heartbeat-*,synthetics-*', - }; - }); - - it('renders child components when count is truthy', () => { - render( - -
Foo
-
Bar
-
Baz
-
- ); - - expect(screen.getByText('Foo')).toBeInTheDocument(); - expect(screen.getByText('Bar')).toBeInTheDocument(); - expect(screen.getByText('Baz')).toBeInTheDocument(); - }); - - it(`doesn't render child components when count is falsy`, () => { - render( - -
Should not be rendered
-
- ); - expect(screen.queryByText('Should not be rendered')).toBeNull(); - }); - - it(`renders error message when an error occurs`, () => { - const errors: IHttpFetchError[] = [ - new HttpFetchError('There was an error fetching your data.', 'error', {} as any, {} as any, { - body: { message: 'There was an error fetching your data.' }, - }), - ]; - render( - -
Should not appear...
-
- ); - expect(screen.queryByText('Should not appear...')).toBeNull(); - }); - - it('renders loading state if no errors or doc count', () => { - render( - -
Should appear even while loading...
-
- ); - expect(screen.queryByText('Should appear even while loading...')).toBeInTheDocument(); - }); - - it('does not render empty state with appropriate base path and no docs', () => { - statesIndexStatus = { - docCount: 0, - indexExists: true, - indices: 'heartbeat-*,synthetics-*', - }; - const text = 'If this is in the snapshot the test should fail'; - render( - -
{text}
-
- ); - expect(screen.queryByText(text)).toBeNull(); - }); - - it('notifies when index does not exist', () => { - statesIndexStatus.indexExists = false; - - const text = 'This text should not render'; - - render( - -
{text}
-
- ); - expect(screen.queryByText(text)).toBeNull(); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx deleted file mode 100644 index a6fd6579c49f..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx +++ /dev/null @@ -1,73 +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, { Fragment } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { IHttpFetchError } from 'src/core/public'; -import { EmptyStateError } from './empty_state_error'; -import { EmptyStateLoading } from './empty_state_loading'; -import { DataOrIndexMissing } from './data_or_index_missing'; -import { DynamicSettings, StatesIndexStatus } from '../../../../common/runtime_types'; - -interface EmptyStateProps { - children: JSX.Element[] | JSX.Element; - statesIndexStatus: StatesIndexStatus | null; - loading: boolean; - errors?: IHttpFetchError[]; - settings?: DynamicSettings; -} - -export const EmptyStateComponent = ({ - children, - statesIndexStatus, - loading, - errors, - settings, -}: EmptyStateProps) => { - if (errors?.length) { - return ; - } - const { indexExists, docCount } = statesIndexStatus ?? {}; - - const isLoading = loading && (!indexExists || docCount === 0 || !statesIndexStatus); - - const noIndicesMessage = ( - {settings?.heartbeatIndices} }} - /> - ); - - const noUptimeDataMessage = ( - {settings?.heartbeatIndices} }} - /> - ); - - if (!indexExists && !isLoading) { - return ; - } else if (indexExists && docCount === 0 && !isLoading) { - return ; - } - /** - * We choose to render the children any time the count > 0, even if - * the component is loading. If we render the loading state for this component, - * it will blow away the state of child components and trigger an ugly - * jittery UX any time the components refresh. This way we'll keep the stale - * state displayed during the fetching process. - */ - return ( - - {isLoading && } -
{children}
-
- ); - // } -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx deleted file mode 100644 index 562e45727dda..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.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 React, { useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { indexStatusAction } from '../../../state/actions'; -import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; -import { EmptyStateComponent } from './index'; -import { UptimeRefreshContext } from '../../../contexts'; -import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; - -export const EmptyState: React.FC = ({ children }) => { - const { data, loading, error } = useSelector(indexStatusSelector); - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { settings } = useSelector(selectDynamicSettings); - - const heartbeatIndices = settings?.heartbeatIndices || ''; - - const dispatch = useDispatch(); - - const noDataInfo = !data || data?.docCount === 0 || data?.indexExists === false; - - useEffect(() => { - if (noDataInfo) { - // only call when we haven't fetched it already - dispatch(indexStatusAction.get()); - } - }, [dispatch, lastRefresh, noDataInfo]); - - useEffect(() => { - // using separate side effect, we want to call index status, - // every statue indices setting changes - dispatch(indexStatusAction.get()); - }, [dispatch, heartbeatIndices]); - - useEffect(() => { - dispatch(getDynamicSettings()); - }, [dispatch]); - - return ( - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts b/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts deleted file mode 100644 index 5ffcc15ed404..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EmptyStateComponent } from './empty_state'; -export { EmptyState } from './empty_state_container'; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx new file mode 100644 index 000000000000..66c68834f285 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.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 { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { indexStatusAction } from '../../../state/actions'; +import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; +import { UptimeRefreshContext } from '../../../contexts'; +import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; + +export const useHasData = () => { + const { loading, error } = useSelector(indexStatusSelector); + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { settings } = useSelector(selectDynamicSettings); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(indexStatusAction.get()); + }, [dispatch, lastRefresh]); + + useEffect(() => { + dispatch(getDynamicSettings()); + }, [dispatch]); + + return { + error, + loading, + settings, + }; +}; diff --git a/x-pack/plugins/uptime/public/components/overview/index.ts b/x-pack/plugins/uptime/public/components/overview/index.ts index d647c38cee1c..6ff4524df527 100644 --- a/x-pack/plugins/uptime/public/components/overview/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/index.ts @@ -6,6 +6,5 @@ */ export * from './monitor_list'; -export * from './empty_state'; export * from './alerts'; export * from './snapshot'; diff --git a/x-pack/plugins/uptime/public/pages/overview.tsx b/x-pack/plugins/uptime/public/pages/overview.tsx index c350c8e1c962..5199eab00f09 100644 --- a/x-pack/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/plugins/uptime/public/pages/overview.tsx @@ -12,7 +12,6 @@ import styled from 'styled-components'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { useTrackPageview } from '../../../observability/public'; import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; -import { EmptyState } from '../components/overview'; import { StatusPanel } from '../components/overview/status_panel'; import { QueryBar } from '../components/overview/query_bar/query_bar'; import { MONITORING_OVERVIEW_LABEL } from '../routes'; @@ -37,7 +36,7 @@ export const OverviewPageComponent = () => { useBreadcrumbs([{ text: MONITORING_OVERVIEW_LABEL }]); // No extra breadcrumbs on overview return ( - + <> @@ -48,6 +47,6 @@ export const OverviewPageComponent = () => { - + ); }; diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index d111d44f08c2..e151e19180dd 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useEffect } from 'react'; -import styled from 'styled-components'; import { Route, Switch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -27,10 +26,8 @@ import { SyntheticsCheckStepsPageHeader, SyntheticsCheckStepsPageRightSideItem, } from './pages/synthetics/synthetics_checks'; -import { ClientPluginsStart } from './apps/plugin'; import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title'; import { UptimeDatePicker } from './components/common/uptime_date_picker'; -import { useKibana } from '../../../../src/plugins/kibana_react/public'; import { CertRefreshBtn } from './components/certificates/cert_refresh_btn'; import { CertificateTitle } from './components/certificates/certificate_title'; import { SyntheticsCallout } from './components/overview/synthetics_callout'; @@ -40,6 +37,7 @@ import { StepDetailPageHeader, StepDetailPageRightSideItem, } from './pages/synthetics/step_detail_page'; +import { UptimePageTemplateComponent } from './apps/uptime_page_template'; interface RouteProps { path: string; @@ -159,17 +157,6 @@ const RouteInit: React.FC> = }; export const PageRouter: FC = () => { - const { - services: { observability }, - } = useKibana(); - const PageTemplateComponent = observability.navigation.PageTemplate; - - const StyledPageTemplateComponent = styled(PageTemplateComponent)` - .euiPageHeaderContent > .euiFlexGroup { - flex-wrap: wrap; - } - `; - return ( {Routes.map( @@ -178,9 +165,9 @@ export const PageRouter: FC = () => {
- + - +
) From b1d6779d43cd8616bb8be88765da5aaa50e87afb Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 21 Sep 2021 08:23:13 -0500 Subject: [PATCH 20/36] [fleet] Introduce Storybook to Fleet (#112611) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../steps/storybooks/build_and_upload.js | 1 + src/dev/storybook/aliases.ts | 1 + .../components/assets_facet_group.stories.tsx | 55 +++++++ .../epm/components/package_card.stories.tsx | 79 ++++++++++ .../sections/epm/components/package_card.tsx | 2 +- .../components/package_list_grid.stories.tsx | 138 ++++++++++++++++++ .../epm/components/package_list_grid.tsx | 2 +- .../epm/components/requirements.stories.tsx | 39 +++++ x-pack/plugins/fleet/storybook/decorator.tsx | 79 ++++++++++ x-pack/plugins/fleet/storybook/main.ts | 20 +++ x-pack/plugins/fleet/storybook/manager.ts | 20 +++ x-pack/plugins/fleet/storybook/preview.tsx | 28 ++++ x-pack/plugins/fleet/tsconfig.json | 1 + 13 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx create mode 100644 x-pack/plugins/fleet/storybook/decorator.tsx create mode 100644 x-pack/plugins/fleet/storybook/main.ts create mode 100644 x-pack/plugins/fleet/storybook/manager.ts create mode 100644 x-pack/plugins/fleet/storybook/preview.tsx diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.js b/.buildkite/scripts/steps/storybooks/build_and_upload.js index 9d72f518837e..c1032575ae4a 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.js +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.js @@ -13,6 +13,7 @@ const STORYBOOKS = [ 'dashboard_enhanced', 'data_enhanced', 'embeddable', + 'fleet', 'infra', 'security_solution', 'ui_actions_enhanced', diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 9395c5fdf883..a61a2618d642 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -24,6 +24,7 @@ export const storybookAliases = { expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', + fleet: 'x-pack/plugins/fleet/storybook', infra: 'x-pack/plugins/infra/.storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook', diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx new file mode 100644 index 000000000000..d98f2b2408d5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { AssetsFacetGroup as Component } from './assets_facet_group'; + +export default { + component: Component, + title: 'Sections/EPM/Assets Facet Group', +}; + +interface Args { + width: number; +} + +const args: Args = { + width: 250, +}; + +export const AssetsFacetGroup = ({ width }: Args) => { + return ( +
+ +
+ ); +}; + +AssetsFacetGroup.args = args; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx new file mode 100644 index 000000000000..e8814b8b8c87 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import type { SavedObject } from 'src/core/public'; + +import type { Installation } from '../../../../../../common'; + +import type { PackageCardProps } from './package_card'; +import { PackageCard } from './package_card'; + +export default { + title: 'Sections/EPM/Package Card', + description: 'A card representing a package available in Fleet', +}; + +type Args = Omit & { width: number }; + +const args: Args = { + width: 250, + title: 'Title', + description: 'Description', + name: 'beats', + release: 'ga', + id: 'id', + version: '1.0.0', + download: '/', + path: 'path', +}; + +const argTypes = { + release: { + control: { + type: 'radio', + options: ['ga', 'beta', 'experimental'], + }, + }, +}; + +export const NotInstalled = ({ width, ...props }: Args) => ( +
+ +
+); + +export const Installed = ({ width, ...props }: Args) => { + const savedObject: SavedObject = { + id: props.id, + type: props.type || '', + attributes: { + name: props.name, + version: props.version, + install_version: props.version, + es_index_patterns: {}, + installed_kibana: [], + installed_es: [], + install_status: 'installed', + install_source: 'registry', + install_started_at: '2020-01-01T00:00:00.000Z', + }, + references: [], + }; + + return ( +
+ +
+ ); +}; + +NotInstalled.args = args; +NotInstalled.argTypes = argTypes; +Installed.args = args; +Installed.argTypes = argTypes; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx index c12e67fdb571..c2d6d0f1e028 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx @@ -15,7 +15,7 @@ import { PackageIcon } from '../../../components'; import { RELEASE_BADGE_LABEL, RELEASE_BADGE_DESCRIPTION } from './release_badge'; -type PackageCardProps = PackageListItem; +export type PackageCardProps = PackageListItem; // adding the `href` causes EuiCard to use a `a` instead of a `button` // `a` tags use `euiLinkColor` which results in blueish Badge text diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx new file mode 100644 index 000000000000..d84e286b6f56 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { action } from '@storybook/addon-actions'; + +import type { SavedObject } from 'src/core/public'; + +import type { Installation } from '../../../../../../common'; + +import type { ListProps } from './package_list_grid'; +import { PackageListGrid } from './package_list_grid'; + +export default { + component: PackageListGrid, + title: 'Sections/EPM/Package List Grid', +}; + +type Args = Pick; + +const args: Args = { + title: 'Installed integrations', + isLoading: false, + showMissingIntegrationMessage: false, +}; + +const savedObject: SavedObject = { + id: 'id', + type: 'integration', + attributes: { + name: 'savedObject', + version: '1.2.3', + install_version: '1.2.3', + es_index_patterns: {}, + installed_kibana: [], + installed_es: [], + install_status: 'installed', + install_source: 'registry', + install_started_at: '2020-01-01T00:00:00.000Z', + }, + references: [], +}; + +export const EmptyList = (props: Args) => ( + +); + +export const List = (props: Args) => ( + +); + +EmptyList.args = args; +List.args = args; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx index 6bbd479c5c2b..db63c5c7dd83 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx @@ -28,7 +28,7 @@ import { useLocalSearch, searchIdField } from '../../../hooks'; import { PackageCard } from './package_card'; -interface ListProps { +export interface ListProps { isLoading?: boolean; controls?: ReactNode; title: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx new file mode 100644 index 000000000000..205d739d4869 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.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 { Requirements as Component } from './requirements'; + +export default { + component: Component, + title: 'Sections/EPM/Requirements', +}; + +interface Args { + width: number; +} + +const args: Args = { + width: 250, +}; + +export const Requirements = ({ width }: Args) => { + return ( +
+ +
+ ); +}; + +Requirements.args = args; diff --git a/x-pack/plugins/fleet/storybook/decorator.tsx b/x-pack/plugins/fleet/storybook/decorator.tsx new file mode 100644 index 000000000000..499b01c2bfba --- /dev/null +++ b/x-pack/plugins/fleet/storybook/decorator.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { of } from 'rxjs'; +import type { DecoratorFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { createMemoryHistory } from 'history'; + +import { I18nProvider } from '@kbn/i18n/react'; + +import { ScopedHistory } from '../../../../src/core/public'; +import { IntegrationsAppContext } from '../public/applications/integrations/app'; +import type { FleetConfigType, FleetStartServices } from '../public/plugin'; + +// TODO: clintandrewhall - this is not ideal, or complete. The root context of Fleet applications +// requires full start contracts of its dependencies. As a result, we have to mock all of those contracts +// with Storybook equivalents. This is a temporary solution, and should be replaced with a more complete +// mock later, (or, ideally, Fleet starts to use a service abstraction). +// +// Expect this to grow as components that are given Stories need access to mocked services. +export const contextDecorator: DecoratorFn = (story: Function) => { + const basepath = '/'; + const memoryHistory = createMemoryHistory({ initialEntries: [basepath] }); + const history = new ScopedHistory(memoryHistory, basepath); + + const startServices = { + application: { + currentAppId$: of('home'), + navigateToUrl: (url: string) => action(`Navigate to: ${url}`), + getUrlForApp: (url: string) => url, + }, + http: { + basePath: { + prepend: () => basepath, + }, + }, + notifications: {}, + history, + uiSettings: { + get$: (key: string) => { + switch (key) { + case 'theme:darkMode': + return of(false); + default: + return of(); + } + }, + }, + i18n: { + Context: I18nProvider, + }, + } as unknown as FleetStartServices; + + const config = { + enabled: true, + agents: { + enabled: true, + elasticsearch: {}, + }, + } as unknown as FleetConfigType; + + const extensions = {}; + + const kibanaVersion = '1.2.3'; + + return ( + + {story()} + + ); +}; diff --git a/x-pack/plugins/fleet/storybook/main.ts b/x-pack/plugins/fleet/storybook/main.ts new file mode 100644 index 000000000000..2cf4124c6178 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/main.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Configuration } from 'webpack'; +import { defaultConfig, WebpackConfig } from '@kbn/storybook'; + +module.exports = { + ...defaultConfig, + addons: ['@storybook/addon-essentials'], + babel: () => ({ + presets: [require.resolve('@kbn/babel-preset/webpack_preset')], + }), + webpackFinal: (config: Configuration) => { + return WebpackConfig({ config }); + }, +}; diff --git a/x-pack/plugins/fleet/storybook/manager.ts b/x-pack/plugins/fleet/storybook/manager.ts new file mode 100644 index 000000000000..471a735ed370 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/manager.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { addons } from '@storybook/addons'; +import { create } from '@storybook/theming'; +import { PANEL_ID } from '@storybook/addon-actions'; + +addons.setConfig({ + theme: create({ + base: 'light', + brandTitle: 'Kibana Fleet Storybook', + brandUrl: 'https://github.com/elastic/kibana/tree/master/x-pack/plugins/fleet', + }), + showPanel: true.valueOf, + selectedPanel: PANEL_ID, +}); diff --git a/x-pack/plugins/fleet/storybook/preview.tsx b/x-pack/plugins/fleet/storybook/preview.tsx new file mode 100644 index 000000000000..a50ff2faaff5 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/preview.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 React from 'react'; +import { addDecorator } from '@storybook/react'; +import { Title, Subtitle, Description, Primary, Stories } from '@storybook/addon-docs/blocks'; + +import { contextDecorator } from './decorator'; + +addDecorator(contextDecorator); + +export const parameters = { + docs: { + page: () => ( + <> + + <Subtitle /> + <Description /> + <Primary /> + <Stories /> + </> + ), + }, +}; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 5002bf289387..a9dd66ce503a 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -14,6 +14,7 @@ "server/**/*.json", "scripts/**/*", "package.json", + "storybook/**/*", "../../../typings/**/*" ], "references": [ From 119c742185a5649e17efbdbaa67185da7aabb5c1 Mon Sep 17 00:00:00 2001 From: Matthew Kime <matt@mattki.me> Date: Tue, 21 Sep 2021 09:41:47 -0500 Subject: [PATCH 21/36] Data views - saved object client use `resolve` instead of `get` (#108637) * so client - use resolve instead of get Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../errors/data_view_saved_object_conflict.ts | 14 +++++ .../data/common/data_views/errors/index.ts | 1 + .../saved_objects_client_wrapper.test.ts | 55 +++++++++++++++++++ .../saved_objects_client_wrapper.ts | 10 +++- .../saved_objects_client_wrapper.test.ts | 55 +++++++++++++++++++ .../saved_objects_client_wrapper.ts | 7 ++- src/plugins/data/server/index.ts | 1 + 7 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts create mode 100644 src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts create mode 100644 src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts diff --git a/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts b/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts new file mode 100644 index 000000000000..3fcb28165572 --- /dev/null +++ b/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.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 and the 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 class DataViewSavedObjectConflictError extends Error { + constructor(savedObjectId: string) { + super(`Conflict loading DataView saved object, id: ${savedObjectId}`); + this.name = 'DataViewSavedObjectConflictError'; + } +} diff --git a/src/plugins/data/common/data_views/errors/index.ts b/src/plugins/data/common/data_views/errors/index.ts index 63bd1ac5f584..20ff90d3fd6c 100644 --- a/src/plugins/data/common/data_views/errors/index.ts +++ b/src/plugins/data/common/data_views/errors/index.ts @@ -7,3 +7,4 @@ */ export * from './duplicate_index_pattern'; +export * from './data_view_saved_object_conflict'; diff --git a/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts new file mode 100644 index 000000000000..221a18ac7fab --- /dev/null +++ b/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.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 { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper'; +import { savedObjectsServiceMock } from 'src/core/public/mocks'; + +import { DataViewSavedObjectConflictError } from '../../common/data_views'; + +describe('SavedObjectsClientPublicToCommon', () => { + const soClient = savedObjectsServiceMock.createStartContract().client; + + test('get saved object - exactMatch', async () => { + const mockedSavedObject = { + version: 'abc', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - aliasMatch', async () => { + const mockedSavedObject = { + version: 'def', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - conflict', async () => { + const mockedSavedObject = { + version: 'ghi', + }; + + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + + await expect(service.get('index-pattern', '1')).rejects.toThrow( + DataViewSavedObjectConflictError + ); + }); +}); diff --git a/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts b/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts index f55ca3896df3..1db4e3b1ccd2 100644 --- a/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts +++ b/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts @@ -12,9 +12,10 @@ import { SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs, SavedObject, + DataViewSavedObjectConflictError, } from '../../common/data_views'; -type SOClient = Pick<SavedObjectsClient, 'find' | 'get' | 'update' | 'create' | 'delete'>; +type SOClient = Pick<SavedObjectsClient, 'find' | 'resolve' | 'update' | 'create' | 'delete'>; const simpleSavedObjectToSavedObject = <T>(simpleSavedObject: SimpleSavedObject): SavedObject<T> => ({ @@ -33,8 +34,11 @@ export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommo } async get<T = unknown>(type: string, id: string) { - const response = await this.savedObjectClient.get<T>(type, id); - return simpleSavedObjectToSavedObject<T>(response); + const response = await this.savedObjectClient.resolve<T>(type, id); + if (response.outcome === 'conflict') { + throw new DataViewSavedObjectConflictError(id); + } + return simpleSavedObjectToSavedObject<T>(response.saved_object); } async update( type: string, diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts new file mode 100644 index 000000000000..39cf74b90610 --- /dev/null +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.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 { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper'; +import { SavedObjectsClientContract } from 'src/core/server'; + +import { DataViewSavedObjectConflictError } from '../../common/data_views'; + +describe('SavedObjectsClientPublicToCommon', () => { + const soClient = ({ resolve: jest.fn() } as unknown) as SavedObjectsClientContract; + + test('get saved object - exactMatch', async () => { + const mockedSavedObject = { + version: 'abc', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - aliasMatch', async () => { + const mockedSavedObject = { + version: 'def', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - conflict', async () => { + const mockedSavedObject = { + version: 'ghi', + }; + + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + + await expect(service.get('index-pattern', '1')).rejects.toThrow( + DataViewSavedObjectConflictError + ); + }); +}); diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts index 45ad8d85f608..b37648a3f038 100644 --- a/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts @@ -10,6 +10,7 @@ import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; import { SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs, + DataViewSavedObjectConflictError, } from '../../common/data_views'; export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommon { @@ -23,7 +24,11 @@ export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommo } async get<T = unknown>(type: string, id: string) { - return await this.savedObjectClient.get<T>(type, id); + const response = await this.savedObjectClient.resolve<T>(type, id); + if (response.outcome === 'conflict') { + throw new DataViewSavedObjectConflictError(id); + } + return response.saved_object; } async update( type: string, diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 9d2e94bcf15c..a17c66c694b2 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -54,6 +54,7 @@ export { IndexPattern, IndexPatternsService, IndexPatternsService as IndexPatternsCommonService, + DataView, } from '../common'; /** From 9bd61a21fe4a1f69e218b60f8b5ce14dd686c5c4 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 21 Sep 2021 10:58:24 -0400 Subject: [PATCH 22/36] [Security Solution][Endpoint] Policy Details store/routing common changes for Trusted Apps by Policy feature (#112567) The changes were picked from the current draft PR #112239 so that they can be shared with other changes taking place in parallel: - Routing methods for parsing/retrieving url params for policy details page - store selectors/reducers in support of url location management for policy details --- .../public/management/common/routing.ts | 48 +++++++++++++++++++ .../policy/store/policy_details/reducer.ts | 23 +++++++++ .../policy/store/policy_details/selectors.ts | 9 +++- .../public/management/pages/policy/types.ts | 21 ++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index b4776f328cf1..58fbd64faf8a 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -26,6 +26,7 @@ import { appendSearch } from '../../common/components/link_to/helpers'; import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types'; import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; import { EventFiltersPageLocation } from '../pages/event_filters/types'; +import { PolicyDetailsArtifactsPageLocation } from '../pages/policy/types'; // Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 type ExactKeys<T1, T2> = Exclude<keyof T1, keyof T2> extends never ? T1 : never; @@ -160,6 +161,25 @@ const normalizeTrustedAppsPageLocation = ( } }; +const normalizePolicyDetailsArtifactsListPageLocation = ( + location?: Partial<PolicyDetailsArtifactsPageLocation> +): Partial<PolicyDetailsArtifactsPageLocation> => { + if (location) { + return { + ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE) + ? { page_index: location.page_index } + : {}), + ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) + ? { page_size: location.page_size } + : {}), + ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), + ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), + }; + } else { + return {}; + } +}; + const normalizeEventFiltersPageLocation = ( location?: Partial<EventFiltersPageLocation> ): Partial<EventFiltersPageLocation> => { @@ -257,6 +277,34 @@ export const getTrustedAppsListPath = (location?: Partial<TrustedAppsListPageLoc )}`; }; +export const extractPolicyDetailsArtifactsListPageLocation = ( + query: querystring.ParsedUrlQuery +): PolicyDetailsArtifactsPageLocation => { + const showParamValue = extractFirstParamValue( + query, + 'show' + ) as PolicyDetailsArtifactsPageLocation['show']; + + return { + ...extractListPaginationParams(query), + show: showParamValue && 'list' === showParamValue ? showParamValue : undefined, + }; +}; + +export const getPolicyDetailsArtifactsListPath = ( + policyId: string, + location?: Partial<PolicyDetailsArtifactsPageLocation> +): string => { + const path = generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH, { + tabName: AdministrationSubTab.policies, + policyId, + }); + + return `${path}${appendSearch( + querystring.stringify(normalizePolicyDetailsArtifactsListPageLocation(location)) + )}`; +}; + export const extractEventFiltetrsPageLocation = ( query: querystring.ParsedUrlQuery ): EventFiltersPageLocation => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts index 512059e9c3aa..cfd808fb9b62 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts @@ -5,6 +5,8 @@ * 2.0. */ +// eslint-disable-next-line import/no-nodejs-modules +import { parse } from 'querystring'; import { fullPolicy, isOnPolicyDetailsPage, license } from './selectors'; import { Immutable, @@ -15,6 +17,12 @@ import { import { ImmutableReducer } from '../../../../../common/store'; import { AppAction } from '../../../../../common/store/actions'; import { PolicyDetailsState } from '../../types'; +import { + MANAGEMENT_DEFAULT_PAGE, + MANAGEMENT_DEFAULT_PAGE_SIZE, +} from '../../../../common/constants'; +import { extractPolicyDetailsArtifactsListPageLocation } from '../../../../common/routing'; +import { createUninitialisedResourceState } from '../../../../state'; const updatePolicyConfigInPolicyData = ( policyData: Immutable<PolicyData>, @@ -47,6 +55,15 @@ export const initialPolicyDetailsState: () => Immutable<PolicyDetailsState> = () total: 0, other: 0, }, + artifacts: { + location: { + page_index: MANAGEMENT_DEFAULT_PAGE, + page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: undefined, + filter: '', + }, + availableList: createUninitialisedResourceState(), + }, }); export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( @@ -106,6 +123,12 @@ export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppActio const newState: Immutable<PolicyDetailsState> = { ...state, location: action.payload, + artifacts: { + ...state.artifacts, + location: extractPolicyDetailsArtifactsListPageLocation( + parse(action.payload.search.slice(1)) + ), + }, }; const isCurrentlyOnDetailsPage = isOnPolicyDetailsPage(newState); const wasPreviouslyOnDetailsPage = isOnPolicyDetailsPage(state); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts index 017111da8d88..1aae538cbf47 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts @@ -9,7 +9,7 @@ import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; import { ILicense } from '../../../../../../../licensing/common/types'; import { unsetPolicyFeaturesAccordingToLicenseLevel } from '../../../../../../common/license/policy_config'; -import { PolicyDetailsState } from '../../types'; +import { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../../types'; import { Immutable, NewPolicyData, @@ -80,6 +80,13 @@ export const needsToRefresh = (state: Immutable<PolicyDetailsState>): boolean => return !state.policyItem && !state.apiError; }; +/** + * Returns current artifacts location + */ +export const getCurrentArtifactsLocation = ( + state: Immutable<PolicyDetailsState> +): Immutable<PolicyDetailsArtifactsPageLocation> => state.artifacts.location; + /** Returns a boolean of whether the user is on the policy form page or not */ export const isOnPolicyFormPage = (state: Immutable<PolicyDetailsState>) => { return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts index 6d767df73cd1..14d740510d25 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts @@ -21,6 +21,8 @@ import { GetPackagesResponse, UpdatePackagePolicyResponse, } from '../../../../../fleet/common'; +import { AsyncResourceState } from '../../state'; +import { TrustedAppsListData } from '../trusted_apps/state'; /** * Policy list store state @@ -61,6 +63,8 @@ export interface PolicyDetailsState { isLoading: boolean; /** current location of the application */ location?: Immutable<AppLocation>; + /** artifacts namespace inside policy details page */ + artifacts: PolicyArtifactsState; /** A summary of stats for the agents associated with a given Fleet Agent Policy */ agentStatusSummary?: Omit<GetAgentStatusResponse['results'], 'updating'>; /** Status of an update to the policy */ @@ -72,12 +76,29 @@ export interface PolicyDetailsState { license?: ILicense; } +/** + * Policy artifacts store state + */ +export interface PolicyArtifactsState { + /** artifacts location params */ + location: PolicyDetailsArtifactsPageLocation; + /** A list of artifacts can be linked to the policy */ + availableList: AsyncResourceState<TrustedAppsListData>; +} + export enum OS { windows = 'windows', mac = 'mac', linux = 'linux', } +export interface PolicyDetailsArtifactsPageLocation { + page_index: number; + page_size: number; + show?: 'list'; + filter: string; +} + /** * Returns the keys of an object whose values meet a criteria. * Ex) interface largeNestedObject = { From 2e563510969cefd21cd3b614b08364a52418219e Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 21 Sep 2021 11:24:25 -0400 Subject: [PATCH 23/36] [Security solution][endpoint] Fix Endpoint List KQL bar not updating the list when the filter is removed (#112595) * Fix search bar `onQuerySubmit` to drop prior `admin_query` param when pushing a new route change * enhance current test case to ensure existing data is also changed when its blank --- .../pages/endpoint_hosts/view/components/search_bar.test.tsx | 2 +- .../pages/endpoint_hosts/view/components/search_bar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx index 624a3c265c4c..1cb21c7da170 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx @@ -92,7 +92,7 @@ describe('when rendering the endpoint list `AdminSearchBar`', () => { ])( 'should update the url and exclude the `admin_query` param when %s was entered', async (_, value) => { - await render(); + await render({ admin_query: "(language:kuery,query:'foo')" }); await submitQuery(value); expect(getQueryParamsFromStore().admin_query).toBe(undefined); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index 2f2a1666b6f5..18d22e0cd1b1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -24,7 +24,7 @@ const AdminQueryBar = styled.div` export const AdminSearchBar = memo(() => { const history = useHistory(); - const queryParams = useEndpointSelector(selectors.uiQueryParams); + const { admin_query: _, ...queryParams } = useEndpointSelector(selectors.uiQueryParams); const searchBarIndexPatterns = useEndpointSelector(selectors.patterns); const searchBarQuery = useEndpointSelector(selectors.searchBarQuery); const clonedIndexPatterns = useMemo( From 924ad664507a153025fb0d73c27e35d5495a4bbb Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Tue, 21 Sep 2021 11:30:20 -0400 Subject: [PATCH 24/36] [CI] Bump agent size for jest integration tests in Buildkite (#112672) --- .buildkite/pipelines/es_snapshots/verify.yml | 2 +- .buildkite/pipelines/hourly.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 10c996c5acec..9af2e938db49 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -64,7 +64,7 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' agents: - queue: jest + queue: n2-4 timeout_in_minutes: 120 key: jest-integration retry: diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index b78db4698c01..e8480b2d3b75 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -118,14 +118,14 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' agents: - queue: jest + queue: n2-4 timeout_in_minutes: 120 key: jest-integration - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' agents: - queue: jest + queue: n2-2 timeout_in_minutes: 120 key: api-integration From 14b2157990df733568032eff98ce6d7119382592 Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Tue, 21 Sep 2021 11:31:15 -0400 Subject: [PATCH 25/36] [CI] Disable tracked branch jobs in Jenkins, enable reporting in Buildkite (#112604) --- .buildkite/scripts/lifecycle/post_command.sh | 3 +-- Jenkinsfile | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.buildkite/scripts/lifecycle/post_command.sh b/.buildkite/scripts/lifecycle/post_command.sh index 14391fdd1e69..23f44a586e97 100755 --- a/.buildkite/scripts/lifecycle/post_command.sh +++ b/.buildkite/scripts/lifecycle/post_command.sh @@ -22,6 +22,5 @@ if [[ "$IS_TEST_EXECUTION_STEP" == "true" ]]; then buildkite-agent artifact upload 'x-pack/test/functional/failure_debug/html/*.html' buildkite-agent artifact upload '.es/**/*.hprof' - # TODO - re-enable when Jenkins is disabled - # node scripts/report_failed_tests --build-url="${BUILDKITE_BUILD_URL}#${BUILDKITE_JOB_ID}" 'target/junit/**/*.xml' + node scripts/report_failed_tests --build-url="${BUILDKITE_BUILD_URL}#${BUILDKITE_JOB_ID}" 'target/junit/**/*.xml' fi diff --git a/Jenkinsfile b/Jenkinsfile index db5ae306e6e2..7dd3d0f41d27 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,10 @@ #!/bin/groovy +if (!env.ghprbPullId) { + print "Non-PR builds are now in Buildkite." + return +} + library 'kibana-pipeline-library' kibanaLibrary.load() From 04e5a154642e12b31831ffade5ee633fa15fe1d4 Mon Sep 17 00:00:00 2001 From: Lisa Cawley <lcawley@elastic.co> Date: Tue, 21 Sep 2021 08:33:48 -0700 Subject: [PATCH 26/36] [DOCS] Add machine learning sync API (#112033) --- docs/api/machine-learning.asciidoc | 11 ++++ docs/api/machine-learning/sync.asciidoc | 79 +++++++++++++++++++++++++ docs/user/api.asciidoc | 1 + 3 files changed, 91 insertions(+) create mode 100644 docs/api/machine-learning.asciidoc create mode 100644 docs/api/machine-learning/sync.asciidoc diff --git a/docs/api/machine-learning.asciidoc b/docs/api/machine-learning.asciidoc new file mode 100644 index 000000000000..265896e6340d --- /dev/null +++ b/docs/api/machine-learning.asciidoc @@ -0,0 +1,11 @@ +[[machine-learning-api]] +== {ml-cap} APIs + +//Manage {kib} saved objects, including dashboards, visualizations, and more. + +The following {ml} API is available: + +* <<machine-learning-api-sync, Sync API>> +//to retrieve a single {kib} saved object by ID + +include::machine-learning/sync.asciidoc[] diff --git a/docs/api/machine-learning/sync.asciidoc b/docs/api/machine-learning/sync.asciidoc new file mode 100644 index 000000000000..5f19bc17ab2f --- /dev/null +++ b/docs/api/machine-learning/sync.asciidoc @@ -0,0 +1,79 @@ +[[machine-learning-api-sync]] +=== Sync {ml} saved objects API +++++ +<titleabbrev>Sync {ml} saved objects</titleabbrev> +++++ + +Synchronizes {kib} saved objects for {ml} jobs. + +[[machine-learning-api-sync-request]] +==== Request + +`GET <kibana host>:<port>/api/ml/saved_objects/sync` + +`GET <kibana host>:<port>/s/<space_id>/api/ml/saved_objects/sync` + + +[[machine-learning-api-sync-path-params]] +==== Path parameters + +`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]] +==== Query parameters + +`simulate`:: +(Optional, boolean) When `true`, simulates the synchronization by only returning +the list actions that _would_ be performed. + +[[machine-learning-api-sync-response-body]] +==== Response body + +`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 saved objects exist for {dfeeds} that no longer exist, they are +deleted. This list contains the {dfeed} identifiers and indicates whether the +synchronization was successful. + +`savedObjectsCreated`:: +(array) If saved objects are missing for {ml} jobs, they are created. This +list contains the job identifiers and indicates whether the synchronization was +successful. + +`savedObjectsDeleted`:: +(array) If saved objects exist for jobs that no longer exist, they are deleted. +This list contains the job identifiers and indicates whether the synchronization +was successful. + +[[machine-learning-api-sync-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[machine-learning-api-sync-example]] +==== Example + +Retrieve the list of {ml} saved objects that require synchronization: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/ml/saved_objects/sync?simulate=true +-------------------------------------------------- +// KIBANA + +If there are two jobs and a {dfeed} that need to be synchronized, for example, +the API returns the following: + +[source,sh] +-------------------------------------------------- +{{"savedObjectsCreated":{"myjob1":{"success":true},"myjob2":{"success":true}},"savedObjectsDeleted":{},"datafeedsAdded":{},"datafeedsRemoved":{"myfeed3":{"success":true}}} +-------------------------------------------------- + +To perform the synchronization, re-run the API and omit the `simulate` parameter. \ No newline at end of file diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index 00aa3c545df6..12e200bb0ba2 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -96,6 +96,7 @@ include::{kib-repo-dir}/api/alerting.asciidoc[] include::{kib-repo-dir}/api/actions-and-connectors.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.asciidoc[] include::{kib-repo-dir}/api/url-shortening.asciidoc[] include::{kib-repo-dir}/api/task-manager/health.asciidoc[] include::{kib-repo-dir}/api/upgrade-assistant.asciidoc[] From fd0f42386f9815082b79e74a47afde05cbc91641 Mon Sep 17 00:00:00 2001 From: James Rodewig <40268737+jrodewig@users.noreply.github.com> Date: Tue, 21 Sep 2021 12:03:23 -0400 Subject: [PATCH 27/36] Update index settings link for Snapshot and Restore (#112597) Updates the docs help link for index settings in the Snapshot and Restore feature. Currently, the link points to https://www.elastic.co/guide/en/elasticsearch/reference/7.14/snapshots-restore-snapshot.html#change-index-settings-during-restore, which primarily documents related restore snapshot API parameters. With elastic/elasticsearch#76929, this section was removed. The new link covers all available index settings, which is a bit more relevant. --- src/core/public/doc_links/doc_links_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 72fa6c5553f7..c86a856577c3 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -387,7 +387,7 @@ export class DocLinksService { }, snapshotRestore: { guide: `${KIBANA_DOCS}snapshot-repositories.html`, - changeIndexSettings: `${ELASTICSEARCH_DOCS}snapshots-restore-snapshot.html#change-index-settings-during-restore`, + changeIndexSettings: `${ELASTICSEARCH_DOCS}index-modules.html`, createSnapshot: `${ELASTICSEARCH_DOCS}snapshots-take-snapshot.html`, getSnapshot: `${ELASTICSEARCH_DOCS}get-snapshot-api.html`, registerSharedFileSystem: `${ELASTICSEARCH_DOCS}snapshots-register-repository.html#snapshots-filesystem-repository`, From d37ad90e53e727adcb208b1fad3a253e69596843 Mon Sep 17 00:00:00 2001 From: Mat Schaffer <mat@elastic.co> Date: Wed, 22 Sep 2021 01:31:02 +0900 Subject: [PATCH 28/36] [Stack Monitoring][Angular removal] No-data page (#110432) * Basic no-data page * Rename to NoDataPage * Add getData property to pass into EuiSuperDatePicker from pages * Wire getData and redirect for no data page * Draft port of isLoading & model updating * Add todo on handling checkers * Switch to model as state object * Add checkers * Porting enabler * Fix build checks * Attempting to smooth out enablement * Clean up CI errors * Fix breadcrumbs * Fix linter warning * Fix checkers dependency (I hope) * Hook up catchReason * Add a stub for react setup mode * Clean warnings * Fix toggleSetupMode by calling initSetupModeState first * Translating checker strings * typo on "xpack" * Move isCollection/reason check in NoData This replicates how the angular app did selective re-rendering of the react component, but while still being able to render the component. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../monitoring/public/application/index.tsx | 7 +- .../application/pages/no_data/enabler.ts | 57 +++++ .../public/application/pages/no_data/index.ts | 8 + .../pages/no_data/no_data_page.tsx | 240 ++++++++++++++++++ .../public/components/no_data/no_data.js | 4 +- .../monitoring/public/lib/setup_mode.tsx | 2 + 6 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/index.ts create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index 6db934303523..8cd5bc3088ac 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -18,6 +18,7 @@ import { GlobalStateProvider } from './global_state_context'; import { ExternalConfigContext, ExternalConfig } from './external_config_context'; import { createPreserveQueryHistory } from './preserve_query_history'; import { RouteInit } from './route_init'; +import { NoDataPage } from './pages/no_data'; import { ElasticsearchOverviewPage } from './pages/elasticsearch/overview'; import { CODE_PATH_ELASTICSEARCH } from '../../common/constants'; import { MonitoringTimeContainer } from './hooks/use_monitoring_time'; @@ -54,7 +55,7 @@ const MonitoringApp: React.FC<{ <BreadcrumbContainer.Provider history={history}> <Router history={history}> <Switch> - <Route path="/no-data" component={NoData} /> + <Route path="/no-data" component={NoDataPage} /> <Route path="/loading" component={LoadingPage} /> <RouteInit path="/license" @@ -98,10 +99,6 @@ const MonitoringApp: React.FC<{ ); }; -const NoData: React.FC<{}> = () => { - return <div>No data page</div>; -}; - const Home: React.FC<{}> = () => { return <div>Home page (Cluster listing)</div>; }; diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts b/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts new file mode 100644 index 000000000000..6225e3863b2f --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.js +export class Enabler { + http: any; + updateModel: any; + + constructor(http: any, updateModel: (properties: any) => void) { + this.http = http; + this.updateModel = updateModel; + } + + async enableCollectionInterval() { + try { + this.updateModel({ isCollectionIntervalUpdating: true }); + + await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_interval', { + method: 'PUT', + }); + this.updateModel({ + isCollectionIntervalUpdated: true, + isCollectionIntervalUpdating: false, + }); + } catch (err) { + this.updateModel({ + errors: (err as any).data, + isCollectionIntervalUpdated: false, + isCollectionIntervalUpdating: false, + }); + } + } + + async enableCollectionEnabled() { + try { + this.updateModel({ isCollectionEnabledUpdating: true }); + await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_enabled', { + method: 'PUT', + }); + + this.updateModel({ + isCollectionEnabledUpdated: true, + isCollectionEnabledUpdating: false, + }); + } catch (err) { + this.updateModel({ + errors: (err as any).data, + isCollectionEnabledUpdated: false, + isCollectionEnabledUpdating: false, + }); + } + } +} diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/index.ts b/x-pack/plugins/monitoring/public/application/pages/no_data/index.ts new file mode 100644 index 000000000000..7fa176d0e6ad --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/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 { NoDataPage } from './no_data_page'; diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx new file mode 100644 index 000000000000..b05bd783b2ff --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -0,0 +1,240 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, useContext, useState } from 'react'; +import { Redirect } from 'react-router-dom'; + +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { NoData } from '../../../components/no_data'; +import { PageTemplate } from '../page_template'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; +import { Legacy } from '../../../legacy_shims'; +import { Enabler } from './enabler'; +import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { initSetupModeState } from '../../setup_mode/setup_mode'; +import { GlobalStateContext } from '../../global_state_context'; + +const CODE_PATHS = [CODE_PATH_LICENSE]; + +interface NoDataPageSetupDeps { + http: any; + data: any; +} + +interface SettingsChecker { + message: string; + api: string; + next?: SettingsChecker; +} + +const clusterCheckers: SettingsChecker[] = [ + { + message: i18n.translate('xpack.monitoring.noData.checker.clusterSettings', { + defaultMessage: 'Checking cluster settings API on production cluster', + }), + api: '../api/monitoring/v1/elasticsearch_settings/check/cluster', + }, + { + message: i18n.translate('xpack.monitoring.noData.checker.nodesSettings', { + defaultMessage: 'Checking nodes settings API on production cluster', + }), + api: '../api/monitoring/v1/elasticsearch_settings/check/nodes', + }, +]; + +export const NoDataPage = () => { + const title = i18n.translate('xpack.monitoring.noData.routeTitle', { + defaultMessage: 'Setup Monitoring', + }); + + const { services } = useKibana<NoDataPageSetupDeps>(); + const [shouldRedirect, setShouldRedirect] = useState(false); + + const [model, setModel] = useState({ + errors: [], // errors can happen from trying to check or set ES settings + checkMessage: null, // message to show while waiting for api response + isLoading: true, // flag for in-progress state of checking for no data reason + isCollectionEnabledUpdating: false, // flags to indicate whether to show a spinner while waiting for ajax + isCollectionEnabledUpdated: false, + isCollectionIntervalUpdating: false, + isCollectionIntervalUpdated: false, + } as any); + + const { update: updateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + updateBreadcrumbs([ + { + 'data-test-subj': 'breadcrumbClusters', + text: 'Clusters', + href: '#/home', + ignoreGlobalState: true, + }, + ]); + + const globalState = useContext(GlobalStateContext); + initSetupModeState(globalState, services.http); + + // From x-pack/plugins/monitoring/public/views/no_data/model_updater.js + const updateModel = useCallback( + (properties: any) => { + setModel((previousModel: any) => { + const updated = { ...previousModel }; + const keys = Object.keys(properties); + + keys.forEach((key) => { + if (Array.isArray(updated[key])) { + updated[key].push(properties[key]); + } else { + updated[key] = properties[key]; + } + }); + + return updated; + }); + }, + [setModel] + ); + + const getPageData = useCallback(async () => { + let catchReason; + try { + const clusters = await getClusters(services); + + if (clusters && clusters.length) { + setShouldRedirect(true); + return; + } + } catch (err) { + if (err && err.status === 503) { + catchReason = { + property: 'custom', + message: err.data.message, + }; + } + } + + if (catchReason) { + updateModel({ reason: catchReason }); + } else { + await startChecks(clusterCheckers, services.http, updateModel); + } + }, [services, updateModel]); + + const enabler = new Enabler(services.http, updateModel); + + return ( + <PageTemplate title={title} getPageData={getPageData}> + {shouldRedirect ? ( + <Redirect to="/home" /> + ) : ( + <NoData {...model} enabler={enabler} isCloudEnabled={Legacy.shims.isCloud} /> + )} + </PageTemplate> + ); +}; + +async function getClusters(services: NoDataPageSetupDeps): Promise<any[]> { + const url = '../api/monitoring/v1/clusters'; + const bounds = services.data?.query.timefilter.timefilter.getBounds(); + const min = bounds.min.toISOString(); + const max = bounds.max.toISOString(); + + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + css: undefined, + timeRange: { + min, + max, + }, + codePaths: CODE_PATHS, + }), + }); + + return formatClusters(response); +} + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js +const mapCheckers = (_checkers: SettingsChecker[]) => { + return _checkers.map((current, checkerIndex) => { + const next = _checkers[checkerIndex + 1]; + if (next !== undefined) { + current.next = next; + } + + return current; + }); +}; + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js +function startChecks( + checkers: SettingsChecker[], + http: { fetch: any }, + updateModel: (properties: any) => void +) { + const runCheck = async (currentChecker: SettingsChecker): Promise<any> => { + updateModel({ checkMessage: currentChecker.message }); + + const { found, reason, error, errorReason } = await executeCheck(currentChecker, http); + + if (error) { + updateModel({ errors: errorReason }); + if (currentChecker.next) { + return runCheck(currentChecker.next); + } + } else if (found) { + return updateModel({ + reason, + isLoading: false, + checkMessage: null, + }); + } else if (currentChecker.next) { + return runCheck(currentChecker.next); + } + + // dead end + updateModel({ + reason: null, + isLoading: false, + checkMessage: null, + }); + }; + + const _checkers = mapCheckers(checkers); + return runCheck(_checkers[0]); +} + +async function executeCheck(checker: SettingsChecker, http: { fetch: any }): Promise<any> { + try { + const response = await http.fetch(checker.api, { + method: 'GET', + }); + const { found, reason } = response; + + return { found, reason }; + } catch (err: any) { + const { data } = err; + + return { + error: true, + found: false, + errorReason: data, + }; + } +} + +function formatClusters(clusters: any): any[] { + return clusters.map(formatCluster); +} + +function formatCluster(cluster: any) { + if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) { + cluster.cluster_name = 'Standalone Cluster'; + } + return cluster; +} diff --git a/x-pack/plugins/monitoring/public/components/no_data/no_data.js b/x-pack/plugins/monitoring/public/components/no_data/no_data.js index 1714ace7ceff..97bf7cacf53e 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/no_data.js +++ b/x-pack/plugins/monitoring/public/components/no_data/no_data.js @@ -32,9 +32,9 @@ import { CloudDeployment } from './blurbs'; import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; function NoDataMessage(props) { - const { isLoading, reason, checkMessage } = props; + const { isLoading, reason, checkMessage, isCollectionEnabledUpdated } = props; - if (isLoading) { + if ((isCollectionEnabledUpdated && !reason) || isLoading) { return <CheckingSettings checkMessage={checkMessage} />; } diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx index f622f2944a31..fca7f94731bc 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx @@ -159,6 +159,8 @@ export const disableElasticsearchInternalCollection = async () => { }; export const toggleSetupMode = (inSetupMode: boolean) => { + if (isReactMigrationEnabled()) return setupModeReact.toggleSetupMode(inSetupMode); + checkAngularState(); const globalState = angularState.injector.get('globalState'); From c76082e00699a651d1494978fc3df994be1b59b0 Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Tue, 21 Sep 2021 09:44:56 -0700 Subject: [PATCH 29/36] Fix eslint error Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- .../data/server/data_views/saved_objects_client_wrapper.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts index 39cf74b90610..bbe857894b3f 100644 --- a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts @@ -12,7 +12,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { DataViewSavedObjectConflictError } from '../../common/data_views'; describe('SavedObjectsClientPublicToCommon', () => { - const soClient = ({ resolve: jest.fn() } as unknown) as SavedObjectsClientContract; + const soClient = { resolve: jest.fn() } as unknown as SavedObjectsClientContract; test('get saved object - exactMatch', async () => { const mockedSavedObject = { From 9574b4b86ba336eb78473bd300170a95e2602e6e Mon Sep 17 00:00:00 2001 From: ymao1 <ying.mao@elastic.co> Date: Tue, 21 Sep 2021 12:48:05 -0400 Subject: [PATCH 30/36] Add indicator to rule details page if broken connector (#112017) * Add indicator to rule details page if broken connector * Adding unit test * Updating disabled state and adding warning callout * Adding back the tooltip * eslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/alert_details.test.tsx | 653 ++++++++---------- .../components/alert_details.tsx | 146 +++- 2 files changed, 413 insertions(+), 386 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index d198c82366fb..c7c41ac4e817 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -39,6 +39,10 @@ jest.mock('react-router-dom', () => ({ }), })); +jest.mock('../../../lib/action_connector_api', () => ({ + loadAllActions: jest.fn().mockResolvedValue([]), +})); + jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveAlertsCapability: jest.fn(() => true), @@ -60,24 +64,22 @@ const authorizedConsumers = { }; const recoveryActionGroup: ActionGroup<'recovered'> = { id: 'recovered', name: 'Recovered' }; -describe('alert_details', () => { - // mock Api handlers +const alertType: AlertType = { + id: '.noop', + name: 'No Op', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup, + actionVariables: { context: [], state: [], params: [] }, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + producer: ALERTS_FEATURE_ID, + authorizedConsumers, + enabledInLicense: true, +}; +describe('alert_details', () => { it('renders the alert name as a title', () => { const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -87,19 +89,6 @@ describe('alert_details', () => { it('renders the alert type badge', () => { const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -118,19 +107,6 @@ describe('alert_details', () => { }, }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -155,19 +131,6 @@ describe('alert_details', () => { ], }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - const actionTypes: ActionType[] = [ { id: '.server-log', @@ -212,18 +175,6 @@ describe('alert_details', () => { }, ], }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - minimumLicenseRequired: 'basic', - authorizedConsumers, - enabledInLicense: true, - }; const actionTypes: ActionType[] = [ { id: '.server-log', @@ -273,20 +224,6 @@ describe('alert_details', () => { describe('links', () => { it('links to the app that created the alert', () => { const alert = mockAlert(); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -296,19 +233,6 @@ describe('alert_details', () => { it('links to the Edit flyout', () => { const alert = mockAlert(); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; const pageHeaderProps = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) @@ -316,22 +240,22 @@ describe('alert_details', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); }); }); @@ -341,20 +265,6 @@ describe('disable button', () => { const alert = mockAlert({ enabled: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) @@ -368,55 +278,43 @@ describe('disable button', () => { }); }); - it('should render a enable button when alert is disabled', () => { + it('should render a enable button and empty state when alert is disabled', async () => { const alert = mockAlert({ enabled: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - - const enableButton = shallow( + const wrapper = mountWithIntl( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> - ) - .find(EuiSwitch) - .find('[name="enable"]') - .first(); + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const enableButton = wrapper.find(EuiSwitch).find('[name="enable"]').first(); + const disabledEmptyPrompt = wrapper.find('[data-test-subj="disabledEmptyPrompt"]'); + const disabledEmptyPromptAction = wrapper.find('[data-test-subj="disabledEmptyPromptAction"]'); expect(enableButton.props()).toMatchObject({ checked: false, disabled: false, }); + expect(disabledEmptyPrompt.exists()).toBeTruthy(); + expect(disabledEmptyPromptAction.exists()).toBeTruthy(); + + disabledEmptyPromptAction.first().simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(mockAlertApis.enableAlert).toHaveBeenCalledTimes(1); }); - it('should enable the alert when alert is disabled and button is clicked', () => { + it('should disable the alert when alert is enabled and button is clicked', () => { const alert = mockAlert({ enabled: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -439,24 +337,10 @@ describe('disable button', () => { expect(disableAlert).toHaveBeenCalledTimes(1); }); - it('should disable the alert when alert is enabled and button is clicked', () => { + it('should enable the alert when alert is disabled and button is clicked', () => { const alert = mockAlert({ enabled: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -492,19 +376,6 @@ describe('disable button', () => { }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(); const enableAlert = jest.fn(); const wrapper = mountWithIntl( @@ -565,19 +436,6 @@ describe('disable button', () => { }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(async () => { await new Promise((resolve) => setTimeout(resolve, 6000)); }); @@ -630,27 +488,12 @@ describe('mute button', () => { enabled: true, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: false, disabled: false, @@ -662,27 +505,12 @@ describe('mute button', () => { enabled: true, muteAll: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: true, disabled: false, @@ -694,20 +522,6 @@ describe('mute button', () => { enabled: true, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const muteAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -721,7 +535,6 @@ describe('mute button', () => { .find(EuiSwitch) .find('[name="mute"]') .first(); - enableButton.simulate('click'); const handler = enableButton.prop('onChange'); expect(typeof handler).toEqual('function'); @@ -735,20 +548,6 @@ describe('mute button', () => { enabled: true, muteAll: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const unmuteAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -762,7 +561,6 @@ describe('mute button', () => { .find(EuiSwitch) .find('[name="mute"]') .first(); - enableButton.simulate('click'); const handler = enableButton.prop('onChange'); expect(typeof handler).toEqual('function'); @@ -776,27 +574,12 @@ describe('mute button', () => { enabled: false, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: false, disabled: true, @@ -843,20 +626,6 @@ describe('edit button', () => { }, ], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const pageHeaderProps = shallow( <AlertDetails alert={alert} @@ -869,27 +638,27 @@ describe('edit button', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); it('should not render an edit button when alert editable but actions arent', () => { const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); - hasExecuteActionsCapability.mockReturnValue(false); + hasExecuteActionsCapability.mockReturnValueOnce(false); const alert = mockAlert({ enabled: true, muteAll: false, @@ -902,20 +671,6 @@ describe('edit button', () => { }, ], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - expect( shallow( <AlertDetails @@ -934,26 +689,12 @@ describe('edit button', () => { it('should render an edit button when alert editable but actions arent when there are no actions on the alert', async () => { const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); - hasExecuteActionsCapability.mockReturnValue(false); + hasExecuteActionsCapability.mockReturnValueOnce(false); const alert = mockAlert({ enabled: true, muteAll: false, actions: [], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const pageHeaderProps = shallow( <AlertDetails alert={alert} @@ -966,41 +707,221 @@ describe('edit button', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); }); -describe('refresh button', () => { - it('should call requestRefresh when clicked', () => { - const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, +describe('broken connector indicator', () => { + const actionTypes: ActionType[] = [ + { + id: '.server-log', + name: 'Server log', + enabled: true, + enabledInConfig: true, enabledInLicense: true, - }; + minimumLicenseRequired: 'basic', + }, + ]; + ruleTypeRegistry.has.mockReturnValue(true); + const alertTypeR: AlertTypeModel = { + id: 'my-alert-type', + iconClass: 'test', + description: 'Alert when testing', + documentationUrl: 'https://localhost.local/docs', + validate: () => { + return { errors: {} }; + }, + alertParamsExpression: jest.fn(), + requiresAppContext: false, + }; + ruleTypeRegistry.get.mockReturnValue(alertTypeR); + useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; + const { loadAllActions } = jest.requireMock('../../../lib/action_connector_api'); + loadAllActions.mockResolvedValue([ + { + secrets: {}, + isMissingSecrets: false, + id: 'connector-id-1', + actionTypeId: '.server-log', + name: 'Test connector', + config: {}, + isPreconfigured: false, + }, + { + secrets: {}, + isMissingSecrets: false, + id: 'connector-id-2', + actionTypeId: '.server-log', + name: 'Test connector 2', + config: {}, + isPreconfigured: false, + }, + ]); + it('should not render broken connector indicator or warning if all rule actions connectors exist', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeFalsy(); + expect(brokenConnectorWarningBanner.exists()).toBeFalsy(); + }); + + it('should render broken connector indicator and warning if any rule actions connector does not exist', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-doesnt-exist', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + const brokenConnectorWarningBannerAction = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeTruthy(); + expect(brokenConnectorWarningBanner.exists()).toBeTruthy(); + expect(brokenConnectorWarningBannerAction.exists()).toBeTruthy(); + }); + + it('should render broken connector indicator and warning with no edit button if any rule actions connector does not exist and user has no edit access', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-doesnt-exist', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); + hasExecuteActionsCapability.mockReturnValue(false); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + const brokenConnectorWarningBannerAction = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeTruthy(); + expect(brokenConnectorWarningBanner.exists()).toBeTruthy(); + expect(brokenConnectorWarningBannerAction.exists()).toBeFalsy(); + }); +}); + +describe('refresh button', () => { + it('should call requestRefresh when clicked', async () => { + const alert = mockAlert(); const requestRefresh = jest.fn(); const wrapper = mountWithIntl( <AlertDetails @@ -1012,6 +933,10 @@ describe('refresh button', () => { /> ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); const refreshButton = wrapper.find('[data-test-subj="refreshAlertsButton"]').first(); expect(refreshButton.exists()).toBeTruthy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 2558993a13fe..2b13bdf613d9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -22,13 +22,16 @@ import { EuiButtonEmpty, EuiButton, EuiLoadingSpinner, + EuiIconTip, + EuiEmptyPrompt, + EuiPageTemplate, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AlertExecutionStatusErrorReasons } from '../../../../../../alerting/common'; import { hasAllPrivilege, hasExecuteActionsCapability } from '../../../lib/capabilities'; import { getAlertingSectionBreadcrumb, getAlertDetailsBreadcrumb } from '../../../lib/breadcrumb'; import { getCurrentDocTitle } from '../../../lib/doc_title'; -import { Alert, AlertType, ActionType } from '../../../../types'; +import { Alert, AlertType, ActionType, ActionConnector } from '../../../../types'; import { ComponentOpts as BulkOperationsComponentOpts, withBulkAlertOperations, @@ -40,6 +43,7 @@ import { routeToRuleDetails } from '../../../constants'; import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; import { alertReducer } from '../../alert_form/alert_reducer'; +import { loadAllActions as loadConnectors } from '../../../lib/action_connector_api'; export type AlertDetailsProps = { alert: Alert; @@ -72,6 +76,9 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; + const [hasActionsWithBrokenConnector, setHasActionsWithBrokenConnector] = + useState<boolean>(false); + // Set breadcrumb and page title useEffect(() => { setBreadcrumbs([ @@ -82,6 +89,28 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // Determine if any attached action has an issue with its connector + useEffect(() => { + (async () => { + let loadedConnectors: ActionConnector[] = []; + try { + loadedConnectors = await loadConnectors({ http }); + } catch (err) { + loadedConnectors = []; + } + + if (loadedConnectors.length > 0) { + const hasActionWithBrokenConnector = alert.actions.some( + (action) => !loadedConnectors.find((connector) => connector.id === action.id) + ); + if (setHasActionsWithBrokenConnector) { + setHasActionsWithBrokenConnector(hasActionWithBrokenConnector); + } + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const canExecuteActions = hasExecuteActionsCapability(capabilities); const canSaveAlert = hasAllPrivilege(alert, alertType) && @@ -197,13 +226,27 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ {uniqueActions && uniqueActions.length ? ( <> <EuiText size="s"> - <p> - <FormattedMessage - id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex" - defaultMessage="Actions" + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex" + defaultMessage="Actions" + />{' '} + {hasActionsWithBrokenConnector && ( + <EuiIconTip + data-test-subj="actionWithBrokenConnector" + type="alert" + color="danger" + content={i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsWarningTooltip', + { + defaultMessage: + 'Unable to load one of the connectors associated with this rule. Edit the rule to select a new connector.', + } + )} + position="right" /> - </p> + )} </EuiText> + <EuiSpacer size="xs" /> <EuiFlexGroup wrap gutterSize="s"> {uniqueActions.map((action, index) => ( @@ -358,6 +401,42 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ </EuiFlexItem> </EuiFlexGroup> ) : null} + {hasActionsWithBrokenConnector && ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiSpacer size="s" /> + <EuiCallOut + color="warning" + data-test-subj="actionWithBrokenConnectorWarningBanner" + size="s" + title={i18n.translate( + 'xpack.triggersActionsUI.sections.alertDetails.actionWithBrokenConnectorWarningBannerTitle', + { + defaultMessage: + 'There is an issue with one of the connectors associated with this rule.', + } + )} + > + {hasEditButton && ( + <EuiFlexGroup gutterSize="s" wrap={true}> + <EuiFlexItem grow={false}> + <EuiButton + data-test-subj="actionWithBrokenConnectorWarningBannerEdit" + color="warning" + onClick={() => setEditFlyoutVisibility(true)} + > + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.actionWithBrokenConnectorWarningBannerEditText" + defaultMessage="Edit rule" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + )} + </EuiCallOut> + </EuiFlexItem> + </EuiFlexGroup> + )} <EuiFlexGroup> <EuiFlexItem> {alert.enabled ? ( @@ -370,23 +449,46 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ ) : ( <> <EuiSpacer /> - <EuiCallOut - title={i18n.translate( - 'xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle', - { - defaultMessage: 'Disabled Rule', + <EuiPageTemplate template="empty"> + <EuiEmptyPrompt + data-test-subj="disabledEmptyPrompt" + title={ + <h2> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle" + defaultMessage="Disabled Rule" + /> + </h2> } - )} - color="warning" - iconType="help" - > - <p> - <FormattedMessage - id="xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledRule" - defaultMessage="This rule is disabled and cannot be displayed. Toggle Disable ↑ to activate it." - /> - </p> - </EuiCallOut> + body={ + <> + <p> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledRule" + defaultMessage="This rule is disabled and cannot be displayed." + /> + </p> + </> + } + actions={[ + <EuiButton + data-test-subj="disabledEmptyPromptAction" + color="primary" + fill + disabled={isEnabledUpdating} + onClick={async () => { + setIsEnabledUpdating(true); + setIsEnabled(true); + await enableAlert(alert); + requestRefresh(); + setIsEnabledUpdating(false); + }} + > + Enable + </EuiButton>, + ]} + /> + </EuiPageTemplate> </> )} </EuiFlexItem> From 126b87bd551c1669e665a7a2efa6a0662a85a32a Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Tue, 21 Sep 2021 11:39:23 -0600 Subject: [PATCH 31/36] [Dashboard] [Visualize] Add Search to Legacy Redirect (#112365) added search to legacy redirect URL --- .../listing/dashboard_no_match.tsx | 3 +-- .../components/visualize_no_match.tsx | 2 +- .../apps/dashboard/bwc_shared_urls.ts | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx b/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx index 03f7b0e16222..91361836f59d 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx @@ -23,9 +23,8 @@ export const DashboardNoMatch = ({ history }: { history: RouteComponentProps['hi useEffect(() => { services.restorePreviousUrl(); - const { navigated } = services.urlForwarding.navigateToLegacyKibanaUrl( - history.location.pathname + history.location.pathname + history.location.search ); if (!navigated) { diff --git a/src/plugins/visualize/public/application/components/visualize_no_match.tsx b/src/plugins/visualize/public/application/components/visualize_no_match.tsx index 3b735eb23671..ad993af43008 100644 --- a/src/plugins/visualize/public/application/components/visualize_no_match.tsx +++ b/src/plugins/visualize/public/application/components/visualize_no_match.tsx @@ -24,7 +24,7 @@ export const VisualizeNoMatch = () => { services.restorePreviousUrl(); const { navigated } = services.urlForwarding.navigateToLegacyKibanaUrl( - services.history.location.pathname + services.history.location.pathname + services.history.location.search ); if (!navigated) { diff --git a/test/functional/apps/dashboard/bwc_shared_urls.ts b/test/functional/apps/dashboard/bwc_shared_urls.ts index d40cf03327fd..569cd8e2a67d 100644 --- a/test/functional/apps/dashboard/bwc_shared_urls.ts +++ b/test/functional/apps/dashboard/bwc_shared_urls.ts @@ -86,6 +86,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('6.0 urls', () => { + let savedDashboardId: string; + it('loads an unsaved dashboard', async function () { const url = `${kibanaLegacyBaseUrl}#/dashboard?${urlQuery}`; log.debug(`Navigating to ${url}`); @@ -106,8 +108,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { storeTimeWithDashboard: true, }); - const id = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); - const url = `${kibanaLegacyBaseUrl}#/dashboard/${id}`; + savedDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); + const url = `${kibanaLegacyBaseUrl}#/dashboard/${savedDashboardId}`; log.debug(`Navigating to ${url}`); await browser.get(url, true); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -121,6 +123,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardExpect.selectedLegendColorCount('#F9D9F9', 5); }); + it('loads a saved dashboard with query via dashboard_no_match', async function () { + await PageObjects.dashboard.gotoDashboardLandingPage(); + const currentUrl = await browser.getCurrentUrl(); + const dashboardBaseUrl = currentUrl.substring(0, currentUrl.indexOf('/app/dashboards')); + const url = `${dashboardBaseUrl}/app/dashboards#/dashboard/${savedDashboardId}?_a=(query:(language:kuery,query:'boop'))`; + log.debug(`Navigating to ${url}`); + await browser.get(url); + await PageObjects.header.waitUntilLoadingHasFinished(); + + const query = await queryBar.getQueryString(); + expect(query).to.equal('boop'); + + await dashboardExpect.panelCount(2); + await PageObjects.dashboard.waitForRenderComplete(); + }); + it('uiState in url takes precedence over saved dashboard state', async function () { const id = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); const updatedQuery = urlQuery.replace(/F9D9F9/g, '000000'); From 5408a3e301564e1d3bc3460e2a65ac965106ea14 Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Tue, 21 Sep 2021 11:45:56 -0600 Subject: [PATCH 32/36] [Dashboard] Retain Viewmode State in Session (#112302) * Made dashboard retain viewmode state in session. This means filters and query will be kept over reloads and navigations --- .../hooks/use_dashboard_app_state.ts | 32 ++-- .../lib/dashboard_session_storage.ts | 2 + .../apps/dashboard/dashboard_unsaved_state.ts | 169 +++++++++++------- 3 files changed, 125 insertions(+), 78 deletions(-) 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 1b24062ccd9b..be600cb80214 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 @@ -237,27 +237,25 @@ export const useDashboardAppState = ({ .pipe(debounceTime(DashboardConstants.CHANGE_CHECK_DEBOUNCE)) .subscribe((states) => { const [lastSaved, current] = states; - const unsavedChanges = - current.viewMode === ViewMode.EDIT ? diffDashboardState(lastSaved, current) : {}; - - let savedTimeChanged = false; + const unsavedChanges = diffDashboardState(lastSaved, current); + + const savedTimeChanged = + lastSaved.timeRestore && + !areTimeRangesEqual( + { + from: savedDashboard?.timeFrom, + to: savedDashboard?.timeTo, + }, + timefilter.getTime() + ); /** - * changes to the time filter should only be considered 'unsaved changes' when + * changes to the dashboard should only be considered 'unsaved changes' when * editing the dashboard */ - if (current.viewMode === ViewMode.EDIT) { - savedTimeChanged = - lastSaved.timeRestore && - !areTimeRangesEqual( - { - from: savedDashboard?.timeFrom, - to: savedDashboard?.timeTo, - }, - timefilter.getTime() - ); - } - const hasUnsavedChanges = Object.keys(unsavedChanges).length > 0 || savedTimeChanged; + const hasUnsavedChanges = + current.viewMode === ViewMode.EDIT && + (Object.keys(unsavedChanges).length > 0 || savedTimeChanged); setDashboardAppState((s) => ({ ...s, hasUnsavedChanges })); unsavedChanges.viewMode = current.viewMode; // always push view mode into session store. diff --git a/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts b/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts index 7d0e60c0609a..a696c8bc15b8 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts @@ -11,6 +11,7 @@ import { Storage } from '../../services/kibana_utils'; import { NotificationsStart } from '../../services/core'; import { panelStorageErrorStrings } from '../../dashboard_strings'; import { DashboardState } from '../../types'; +import { ViewMode } from '../../services/embeddable'; export const DASHBOARD_PANELS_UNSAVED_ID = 'unsavedDashboard'; const DASHBOARD_PANELS_SESSION_KEY = 'dashboardStateManagerPanels'; @@ -69,6 +70,7 @@ export class DashboardSessionStorage { const dashboardsWithUnsavedChanges: string[] = []; Object.keys(dashboardStatesInSpace).map((dashboardId) => { if ( + dashboardStatesInSpace[dashboardId].viewMode === ViewMode.EDIT && Object.keys(dashboardStatesInSpace[dashboardId]).some( (stateKey) => stateKey !== 'viewMode' ) diff --git a/test/functional/apps/dashboard/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/dashboard_unsaved_state.ts index 6b71dd34b76f..8043c8bf8cc3 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/dashboard_unsaved_state.ts @@ -12,6 +12,9 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); + const browser = getService('browser'); + const queryBar = getService('queryBar'); + const filterBar = getService('filterBar'); const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); @@ -19,9 +22,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let originalPanelCount = 0; let unsavedPanelCount = 0; + const testQuery = 'Test Query'; - // FLAKY: https://github.com/elastic/kibana/issues/91191 - describe.skip('dashboard unsaved panels', () => { + describe('dashboard unsaved state', () => { before(async () => { await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ @@ -31,79 +34,123 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.loadSavedDashboard('few panels'); await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); originalPanelCount = await PageObjects.dashboard.getPanelCount(); }); - it('does not show unsaved changes badge when there are no unsaved changes', async () => { - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); - }); + describe('view mode state', () => { + before(async () => { + await queryBar.setQuery(testQuery); + await filterBar.addFilter('bytes', 'exists'); + await queryBar.submitQuery(); + }); - it('shows the unsaved changes badge after adding panels', async () => { - await PageObjects.dashboard.switchToEditMode(); - // add an area chart by value - await dashboardAddPanel.clickEditorMenuButton(); - await dashboardAddPanel.clickAggBasedVisualizations(); - await PageObjects.visualize.clickAreaChart(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.visualize.saveVisualizationAndReturn(); + const validateQueryAndFilter = async () => { + const query = await queryBar.getQueryString(); + expect(query).to.eql(testQuery); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.eql(1); + }; + + it('persists after navigating to the listing page and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - // add a metric by reference - await dashboardAddPanel.addVisualization('Rendering-Test: metric'); + it('persists after navigating to Visualize and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.common.navigateToApp('dashboards'); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - }); + it('persists after a hard refresh', async () => { + await browser.refresh(); + const alert = await browser.getAlert(); + await alert?.accept(); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - it('has correct number of panels', async () => { - unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + after(async () => { + // discard changes made in view mode + await PageObjects.dashboard.switchToEditMode(); + await PageObjects.dashboard.clickCancelOutOfEditMode(); + }); }); - it('retains unsaved panel count after navigating to listing page and back', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.gotoDashboardLandingPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.loadSavedDashboard('few panels'); - await PageObjects.dashboard.switchToEditMode(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + describe('edit mode state', () => { + const addPanels = async () => { + // add an area chart by value + await dashboardAddPanel.clickEditorMenuButton(); + await dashboardAddPanel.clickAggBasedVisualizations(); + await PageObjects.visualize.clickAreaChart(); + await PageObjects.visualize.clickNewSearch(); + await PageObjects.visualize.saveVisualizationAndReturn(); + + // add a metric by reference + await dashboardAddPanel.addVisualization('Rendering-Test: metric'); + }; + + it('does not show unsaved changes badge when there are no unsaved changes', async () => { + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); - it('retains unsaved panel count after navigating to another app and back', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.gotoVisualizationLandingPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.common.navigateToApp('dashboards'); - await PageObjects.dashboard.loadSavedDashboard('few panels'); - await PageObjects.dashboard.switchToEditMode(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + it('shows the unsaved changes badge after adding panels', async () => { + await PageObjects.dashboard.switchToEditMode(); + await addPanels(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); + }); - it('resets to original panel count upon entering view mode', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(originalPanelCount); - }); + it('has correct number of panels', async () => { + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + }); - it('shows unsaved changes badge in view mode if changes have not been discarded', async () => { - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - }); + it('retains unsaved panel count after navigating to listing page and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(unsavedPanelCount); + }); - it('retains unsaved panel count after returning to edit mode', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.switchToEditMode(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + it('retains unsaved panel count after navigating to another app and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.common.navigateToApp('dashboards'); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(unsavedPanelCount); + }); - it('does not show unsaved changes badge after saving', async () => { - await PageObjects.dashboard.saveDashboard('Unsaved State Test'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + it('resets to original panel count after discarding changes', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.clickCancelOutOfEditMode(); + await PageObjects.header.waitUntilLoadingHasFinished(); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(originalPanelCount); + expect(PageObjects.dashboard.getIsInViewMode()).to.eql(true); + }); + + it('does not show unsaved changes badge after saving', async () => { + await PageObjects.dashboard.switchToEditMode(); + await addPanels(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.saveDashboard('Unsaved State Test'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); }); }); } From 659b295391a001f77a8f6ed65f4b4b1249214a61 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet <nicolas.chaulet@elastic.co> Date: Tue, 21 Sep 2021 13:52:21 -0400 Subject: [PATCH 33/36] [Fleet] Allow to preconfigure alternative ES outputs (on the same cluster) (#111002) --- docs/settings/fleet-settings.asciidoc | 16 + .../plugins/fleet/common/constants/output.ts | 4 +- .../package_policies_to_agent_inputs.ts | 5 +- x-pack/plugins/fleet/common/types/index.ts | 7 +- .../fleet/common/types/models/agent_policy.ts | 10 +- .../fleet/common/types/models/output.ts | 6 +- .../common/types/models/preconfiguration.ts | 5 + x-pack/plugins/fleet/server/errors/utils.ts | 4 +- x-pack/plugins/fleet/server/index.ts | 7 +- .../fleet/server/saved_objects/index.ts | 4 + .../full_agent_policy.test.ts.snap | 292 ++++++++++++++++++ .../agent_policies/full_agent_policy.test.ts | 256 +++++++++++++++ .../agent_policies/full_agent_policy.ts | 229 ++++++++++++++ .../server/services/agent_policies/index.ts | 8 + .../server/services/agent_policy.test.ts | 121 +------- .../fleet/server/services/agent_policy.ts | 197 +++--------- .../fleet/server/services/output.test.ts | 94 +++++- .../plugins/fleet/server/services/output.ts | 116 +++++-- .../server/services/preconfiguration.test.ts | 178 ++++++++++- .../fleet/server/services/preconfiguration.ts | 92 +++++- x-pack/plugins/fleet/server/services/setup.ts | 30 +- x-pack/plugins/fleet/server/types/index.tsx | 1 + .../types/models/preconfiguration.test.ts | 81 +++++ .../server/types/models/preconfiguration.ts | 127 +++++--- 24 files changed, 1517 insertions(+), 373 deletions(-) create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/index.ts create mode 100644 x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts diff --git a/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index 3ae1c9df616b..3411f3930970 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -86,6 +86,8 @@ Optional properties are: be changed by updating the {kib} config. `is_default`:: If `true`, this policy is the default agent policy. `is_default_fleet_server`:: If `true`, this policy is the default {fleet-server} agent policy. + `data_output_id`:: ID of the output to send data (Need to be identical to `monitoring_output_id`) + `monitoring_output_id`:: ID of the output to send monitoring data. (Need to be identical to `data_output_id`) `package_policies`:: List of integration policies to add to this policy. `name`::: (required) Name of the integration policy. `package`::: (required) Integration that this policy configures @@ -96,6 +98,20 @@ Optional properties are: integration. Follows the same schema as integration inputs, with the exception that any object in `vars` can be passed `frozen: true` in order to prevent that specific `var` from being edited by the user. + +| `xpack.fleet.outputs` + | List of ouputs that are configured when the {fleet} app starts. +Required properties are: + + `id`:: Unique ID for this output. The ID should be a string. + `name`:: Output name. + `type`:: Type of Output. Currently we only support "elasticsearch". + `hosts`:: Array that contains the list of host for that output. + `config`:: Extra config for that output. + +Optional properties are: + + `is_default`:: If `true`, this output is the default output. |=== Example configuration: diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index 80c7e56dbb52..9a236001aca2 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -13,8 +13,10 @@ export const outputType = { Elasticsearch: 'elasticsearch', } as const; +export const DEFAULT_OUTPUT_ID = 'default'; + export const DEFAULT_OUTPUT: NewOutput = { - name: 'default', + name: DEFAULT_OUTPUT_ID, is_default: true, type: outputType.Elasticsearch, hosts: [''], diff --git a/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts index f262521461b9..119bb04af5ca 100644 --- a/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts +++ b/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts @@ -11,7 +11,8 @@ import type { PackagePolicy, FullAgentPolicyInput, FullAgentPolicyInputStream } import { DEFAULT_OUTPUT } from '../constants'; export const storedPackagePoliciesToAgentInputs = ( - packagePolicies: PackagePolicy[] + packagePolicies: PackagePolicy[], + outputId: string = DEFAULT_OUTPUT.name ): FullAgentPolicyInput[] => { const fullInputs: FullAgentPolicyInput[] = []; @@ -32,7 +33,7 @@ export const storedPackagePoliciesToAgentInputs = ( data_stream: { namespace: packagePolicy.namespace || 'default', }, - use_output: DEFAULT_OUTPUT.name, + use_output: outputId, ...(input.compiled_input || {}), ...(input.streams.length ? { diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index 0deda3bf3265..bd970fc2cd83 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -8,7 +8,11 @@ export * from './models'; export * from './rest_spec'; -import type { PreconfiguredAgentPolicy, PreconfiguredPackage } from './models/preconfiguration'; +import type { + PreconfiguredAgentPolicy, + PreconfiguredPackage, + PreconfiguredOutput, +} from './models/preconfiguration'; export interface FleetConfigType { enabled: boolean; @@ -26,6 +30,7 @@ export interface FleetConfigType { }; agentPolicies?: PreconfiguredAgentPolicy[]; packages?: PreconfiguredPackage[]; + outputs?: PreconfiguredOutput[]; agentIdVerificationEnabled?: boolean; } diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index f64467ca674f..3f9e43e72c51 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -23,6 +23,8 @@ export interface NewAgentPolicy { monitoring_enabled?: MonitoringType; unenroll_timeout?: number; is_preconfigured?: boolean; + data_output_id?: string; + monitoring_output_id?: string; } export interface AgentPolicy extends NewAgentPolicy { @@ -71,12 +73,14 @@ export interface FullAgentPolicyOutputPermissions { }; } +export type FullAgentPolicyOutput = Pick<Output, 'type' | 'hosts' | 'ca_sha256' | 'api_key'> & { + [key: string]: any; +}; + export interface FullAgentPolicy { id: string; outputs: { - [key: string]: Pick<Output, 'type' | 'hosts' | 'ca_sha256' | 'api_key'> & { - [key: string]: any; - }; + [key: string]: FullAgentPolicyOutput; }; output_permissions?: { [output: string]: FullAgentPolicyOutputPermissions; diff --git a/x-pack/plugins/fleet/common/types/models/output.ts b/x-pack/plugins/fleet/common/types/models/output.ts index c1dc2a4b4e05..4f70460e89ff 100644 --- a/x-pack/plugins/fleet/common/types/models/output.ts +++ b/x-pack/plugins/fleet/common/types/models/output.ts @@ -17,11 +17,13 @@ export interface NewOutput { hosts?: string[]; ca_sha256?: string; api_key?: string; - config?: Record<string, any>; config_yaml?: string; + is_preconfigured?: boolean; } -export type OutputSOAttributes = NewOutput; +export type OutputSOAttributes = NewOutput & { + output_id?: string; +}; export type Output = NewOutput & { id: string; diff --git a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts index 6087c910510c..17f9b946885b 100644 --- a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts @@ -11,6 +11,7 @@ import type { NewPackagePolicyInput, } from './package_policy'; import type { NewAgentPolicy } from './agent_policy'; +import type { Output } from './output'; export type InputsOverride = Partial<NewPackagePolicyInput> & { vars?: Array<NewPackagePolicyInput['vars'] & { name: string }>; @@ -29,3 +30,7 @@ export interface PreconfiguredAgentPolicy extends Omit<NewAgentPolicy, 'namespac } export type PreconfiguredPackage = Omit<PackagePolicyPackage, 'title'>; + +export interface PreconfiguredOutput extends Omit<Output, 'config_yaml'> { + config?: Record<string, unknown>; +} diff --git a/x-pack/plugins/fleet/server/errors/utils.ts b/x-pack/plugins/fleet/server/errors/utils.ts index 2eae04e05bd6..d58f82b94fcd 100644 --- a/x-pack/plugins/fleet/server/errors/utils.ts +++ b/x-pack/plugins/fleet/server/errors/utils.ts @@ -11,6 +11,6 @@ export function isESClientError(error: unknown): error is ResponseError { return error instanceof ResponseError; } -export const isElasticsearchVersionConflictError = (error: Error): boolean => { +export function isElasticsearchVersionConflictError(error: Error): boolean { return isESClientError(error) && error.meta.statusCode === 409; -}; +} diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 21cdf659f2f5..05ad8a9a9c83 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -9,7 +9,11 @@ import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; import type { PluginConfigDescriptor, PluginInitializerContext } from 'src/core/server'; -import { PreconfiguredPackagesSchema, PreconfiguredAgentPoliciesSchema } from './types'; +import { + PreconfiguredPackagesSchema, + PreconfiguredAgentPoliciesSchema, + PreconfiguredOutputsSchema, +} from './types'; import { FleetPlugin } from './plugin'; @@ -113,6 +117,7 @@ export const config: PluginConfigDescriptor = { }), packages: PreconfiguredPackagesSchema, agentPolicies: PreconfiguredAgentPoliciesSchema, + outputs: PreconfiguredOutputsSchema, agentIdVerificationEnabled: schema.boolean({ defaultValue: true }), }), }; diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 5c117909432b..83188e004704 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -156,6 +156,8 @@ const getSavedObjectTypes = ( revision: { type: 'integer' }, monitoring_enabled: { type: 'keyword', index: false }, is_preconfigured: { type: 'keyword' }, + data_output_id: { type: 'keyword' }, + monitoring_output_id: { type: 'keyword' }, }, }, migrations: { @@ -196,6 +198,7 @@ const getSavedObjectTypes = ( }, mappings: { properties: { + output_id: { type: 'keyword', index: false }, name: { type: 'keyword' }, type: { type: 'keyword' }, is_default: { type: 'boolean' }, @@ -203,6 +206,7 @@ const getSavedObjectTypes = ( ca_sha256: { type: 'keyword', index: false }, config: { type: 'flattened' }, config_yaml: { type: 'text' }, + is_preconfigured: { type: 'boolean', index: false }, }, }, migrations: { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap new file mode 100644 index 000000000000..970bccbafa63 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -0,0 +1,292 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getFullAgentPolicy should support a different data output 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "default", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "data-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "default": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "data-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-data.co:9201", + ], + "type": "elasticsearch", + }, + "default": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://127.0.0.1:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; + +exports[`getFullAgentPolicy should support a different monitoring output 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "monitoring-output-id", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "default": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "monitoring-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "default": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://127.0.0.1:9201", + ], + "type": "elasticsearch", + }, + "monitoring-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-monitoring.co:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; + +exports[`getFullAgentPolicy should support both different outputs for data and monitoring 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "monitoring-output-id", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "data-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "monitoring-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "data-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-data.co:9201", + ], + "type": "elasticsearch", + }, + "monitoring-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-monitoring.co:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts new file mode 100644 index 000000000000..8df1234982ee --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -0,0 +1,256 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsClientMock } from 'src/core/server/mocks'; + +import type { AgentPolicy, Output } from '../../types'; + +import { agentPolicyService } from '../agent_policy'; +import { agentPolicyUpdateEventHandler } from '../agent_policy_update'; + +import { getFullAgentPolicy } from './full_agent_policy'; + +const mockedAgentPolicyService = agentPolicyService as jest.Mocked<typeof agentPolicyService>; + +function mockAgentPolicy(data: Partial<AgentPolicy>) { + mockedAgentPolicyService.get.mockResolvedValue({ + id: 'agent-policy', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + name: 'Policy', + updated_at: '2020-01-01', + updated_by: 'qwerty', + ...data, + }); +} + +jest.mock('../settings', () => { + return { + getSettings: () => { + return { + id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', + fleet_server_hosts: ['http://fleetserver:8220'], + }; + }, + }; +}); + +jest.mock('../agent_policy'); + +jest.mock('../output', () => { + return { + outputService: { + getDefaultOutputId: () => 'test-id', + get: (soClient: any, id: string): Output => { + switch (id) { + case 'data-output-id': + return { + id: 'data-output-id', + is_default: false, + name: 'Data output', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es-data.co:9201'], + }; + case 'monitoring-output-id': + return { + id: 'monitoring-output-id', + is_default: false, + name: 'Monitoring output', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es-monitoring.co:9201'], + }; + default: + return { + id: 'test-id', + is_default: true, + name: 'default', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }; + } + }, + }, + }; +}); + +jest.mock('../agent_policy_update'); +jest.mock('../agents'); +jest.mock('../package_policy'); + +function getAgentPolicyUpdateMock() { + return agentPolicyUpdateEventHandler as unknown as jest.Mock< + typeof agentPolicyUpdateEventHandler + >; +} + +describe('getFullAgentPolicy', () => { + beforeEach(() => { + getAgentPolicyUpdateMock().mockClear(); + mockedAgentPolicyService.get.mockReset(); + }); + + it('should return a policy without monitoring if monitoring is not enabled', async () => { + mockAgentPolicy({ + revision: 1, + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + enabled: false, + logs: false, + metrics: false, + }, + }, + }); + }); + + it('should return a policy with monitoring if monitoring is enabled for logs', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['logs'], + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + namespace: 'default', + use_output: 'default', + enabled: true, + logs: true, + metrics: false, + }, + }, + }); + }); + + it('should return a policy with monitoring if monitoring is enabled for metrics', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + namespace: 'default', + use_output: 'default', + enabled: true, + logs: false, + metrics: true, + }, + }, + }); + }); + + it('should support a different monitoring output', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + monitoring_output_id: 'monitoring-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should support a different data output', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + data_output_id: 'data-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should support both different outputs for data and monitoring ', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + data_output_id: 'data-output-id', + monitoring_output_id: 'monitoring-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should use "default" as the default policy id', async () => { + mockAgentPolicy({ + id: 'policy', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + data_output_id: 'test-id', + monitoring_output_id: 'test-id', + }); + + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy?.outputs.default).toBeDefined(); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts new file mode 100644 index 000000000000..4e8b3a2c1952 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { SavedObjectsClientContract } from 'kibana/server'; +import { safeLoad } from 'js-yaml'; + +import type { + FullAgentPolicy, + PackagePolicy, + Settings, + Output, + FullAgentPolicyOutput, +} from '../../types'; +import { agentPolicyService } from '../agent_policy'; +import { outputService } from '../output'; +import { + storedPackagePoliciesToAgentPermissions, + DEFAULT_PERMISSIONS, +} from '../package_policies_to_agent_permissions'; +import { storedPackagePoliciesToAgentInputs, dataTypes, outputType } from '../../../common'; +import type { FullAgentPolicyOutputPermissions } from '../../../common'; +import { getSettings } from '../settings'; +import { PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, DEFAULT_OUTPUT } from '../../constants'; + +const MONITORING_DATASETS = [ + 'elastic_agent', + 'elastic_agent.elastic_agent', + 'elastic_agent.apm_server', + 'elastic_agent.filebeat', + 'elastic_agent.fleet_server', + 'elastic_agent.metricbeat', + 'elastic_agent.osquerybeat', + 'elastic_agent.packetbeat', + 'elastic_agent.endpoint_security', + 'elastic_agent.auditbeat', + 'elastic_agent.heartbeat', +]; + +export async function getFullAgentPolicy( + soClient: SavedObjectsClientContract, + id: string, + options?: { standalone: boolean } +): Promise<FullAgentPolicy | null> { + let agentPolicy; + const standalone = options?.standalone; + + try { + agentPolicy = await agentPolicyService.get(soClient, id); + } catch (err) { + if (!err.isBoom || err.output.statusCode !== 404) { + throw err; + } + } + + if (!agentPolicy) { + return null; + } + + const defaultOutputId = await outputService.getDefaultOutputId(soClient); + if (!defaultOutputId) { + throw new Error('Default output is not setup'); + } + + const dataOutputId = agentPolicy.data_output_id || defaultOutputId; + const monitoringOutputId = agentPolicy.monitoring_output_id || defaultOutputId; + + const outputs = await Promise.all( + Array.from(new Set([dataOutputId, monitoringOutputId])).map((outputId) => + outputService.get(soClient, outputId) + ) + ); + + const dataOutput = outputs.find((output) => output.id === dataOutputId); + if (!dataOutput) { + throw new Error(`Data output not found ${dataOutputId}`); + } + const monitoringOutput = outputs.find((output) => output.id === monitoringOutputId); + if (!monitoringOutput) { + throw new Error(`Monitoring output not found ${monitoringOutputId}`); + } + + const fullAgentPolicy: FullAgentPolicy = { + id: agentPolicy.id, + outputs: { + ...outputs.reduce<FullAgentPolicy['outputs']>((acc, output) => { + acc[getOutputIdForAgentPolicy(output)] = transformOutputToFullPolicyOutput(output); + + return acc; + }, {}), + }, + inputs: storedPackagePoliciesToAgentInputs( + agentPolicy.package_policies as PackagePolicy[], + getOutputIdForAgentPolicy(dataOutput) + ), + revision: agentPolicy.revision, + ...(agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 + ? { + agent: { + monitoring: { + namespace: agentPolicy.namespace, + use_output: getOutputIdForAgentPolicy(monitoringOutput), + enabled: true, + logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), + metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), + }, + }, + } + : { + agent: { + monitoring: { enabled: false, logs: false, metrics: false }, + }, + }), + }; + + const dataPermissions = (await storedPackagePoliciesToAgentPermissions( + soClient, + agentPolicy.package_policies + )) || { _fallback: DEFAULT_PERMISSIONS }; + + dataPermissions._elastic_agent_checks = { + cluster: DEFAULT_PERMISSIONS.cluster, + }; + + // TODO: fetch this from the elastic agent package https://github.com/elastic/kibana/issues/107738 + const monitoringNamespace = fullAgentPolicy.agent?.monitoring.namespace; + const monitoringPermissions: FullAgentPolicyOutputPermissions = + monitoringOutputId === dataOutputId + ? dataPermissions + : { + _elastic_agent_checks: { + cluster: DEFAULT_PERMISSIONS.cluster, + }, + }; + if ( + fullAgentPolicy.agent?.monitoring.enabled && + monitoringNamespace && + monitoringOutput && + monitoringOutput.type === outputType.Elasticsearch + ) { + let names: string[] = []; + if (fullAgentPolicy.agent.monitoring.logs) { + names = names.concat( + MONITORING_DATASETS.map((dataset) => `logs-${dataset}-${monitoringNamespace}`) + ); + } + if (fullAgentPolicy.agent.monitoring.metrics) { + names = names.concat( + MONITORING_DATASETS.map((dataset) => `metrics-${dataset}-${monitoringNamespace}`) + ); + } + + monitoringPermissions._elastic_agent_checks.indices = [ + { + names, + privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, + }, + ]; + } + + // Only add permissions if output.type is "elasticsearch" + fullAgentPolicy.output_permissions = Object.keys(fullAgentPolicy.outputs).reduce< + NonNullable<FullAgentPolicy['output_permissions']> + >((outputPermissions, outputId) => { + const output = fullAgentPolicy.outputs[outputId]; + if (output && output.type === outputType.Elasticsearch) { + outputPermissions[outputId] = + outputId === getOutputIdForAgentPolicy(dataOutput) + ? dataPermissions + : monitoringPermissions; + } + return outputPermissions; + }, {}); + + // only add settings if not in standalone + if (!standalone) { + let settings: Settings; + try { + settings = await getSettings(soClient); + } catch (error) { + throw new Error('Default settings is not setup'); + } + if (settings.fleet_server_hosts && settings.fleet_server_hosts.length) { + fullAgentPolicy.fleet = { + hosts: settings.fleet_server_hosts, + }; + } + } + return fullAgentPolicy; +} + +function transformOutputToFullPolicyOutput( + output: Output, + standalone = false +): FullAgentPolicyOutput { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { config_yaml, type, hosts, ca_sha256, api_key } = output; + const configJs = config_yaml ? safeLoad(config_yaml) : {}; + const newOutput: FullAgentPolicyOutput = { + type, + hosts, + ca_sha256, + api_key, + ...configJs, + }; + + if (standalone) { + delete newOutput.api_key; + newOutput.username = 'ES_USERNAME'; + newOutput.password = 'ES_PASSWORD'; + } + + return newOutput; +} + +/** + * Get id used in full agent policy (sent to the agents) + * we use "default" for the default policy to avoid breaking changes + */ +function getOutputIdForAgentPolicy(output: Output) { + if (output.is_default) { + return DEFAULT_OUTPUT.name; + } + + return output.id; +} diff --git a/x-pack/plugins/fleet/server/services/agent_policies/index.ts b/x-pack/plugins/fleet/server/services/agent_policies/index.ts new file mode 100644 index 000000000000..b793ed26a08b --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/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 { getFullAgentPolicy } from './full_agent_policy'; diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 59e0f6fd7840..6a5cb28dbaa0 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -7,7 +7,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; -import type { AgentPolicy, NewAgentPolicy, Output } from '../types'; +import type { AgentPolicy, NewAgentPolicy } from '../types'; import { agentPolicyService } from './agent_policy'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; @@ -47,24 +47,6 @@ function getSavedObjectMock(agentPolicyAttributes: any) { return mock; } -jest.mock('./output', () => { - return { - outputService: { - getDefaultOutputId: () => 'test-id', - get: (): Output => { - return { - id: 'test-id', - is_default: true, - name: 'default', - // @ts-ignore - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - }; - }, - }, - }; -}); - jest.mock('./agent_policy_update'); jest.mock('./agents'); jest.mock('./package_policy'); @@ -186,106 +168,17 @@ describe('agent policy', () => { }); }); - describe('getFullAgentPolicy', () => { - it('should return a policy without monitoring if monitoring is not enabled', async () => { - const soClient = getSavedObjectMock({ - revision: 1, - }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); - - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - enabled: false, - logs: false, - metrics: false, - }, - }, - }); - }); - - it('should return a policy with monitoring if monitoring is enabled for logs', async () => { - const soClient = getSavedObjectMock({ - namespace: 'default', - revision: 1, - monitoring_enabled: ['logs'], - }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); - - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - namespace: 'default', - use_output: 'default', - enabled: true, - logs: true, - metrics: false, - }, - }, - }); - }); - - it('should return a policy with monitoring if monitoring is enabled for metrics', async () => { + describe('bumpAllAgentPoliciesForOutput', () => { + it('should call agentPolicyUpdateEventHandler with updated event once', async () => { const soClient = getSavedObjectMock({ - namespace: 'default', revision: 1, monitoring_enabled: ['metrics'], }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - namespace: 'default', - use_output: 'default', - enabled: true, - logs: false, - metrics: true, - }, - }, - }); + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, 'output-id-123'); + + expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 38fb07754bdd..751e981cb808 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -6,7 +6,6 @@ */ import { uniq, omit } from 'lodash'; -import { safeLoad } from 'js-yaml'; import uuid from 'uuid/v4'; import type { ElasticsearchClient, @@ -21,7 +20,6 @@ import { AGENT_POLICY_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, - PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, } from '../constants'; import type { PackagePolicy, @@ -33,52 +31,27 @@ import type { ListWithKuery, NewPackagePolicy, } from '../types'; -import { - agentPolicyStatuses, - storedPackagePoliciesToAgentInputs, - dataTypes, - packageToPackagePolicy, - AGENT_POLICY_INDEX, -} from '../../common'; +import { agentPolicyStatuses, packageToPackagePolicy, AGENT_POLICY_INDEX } from '../../common'; import type { DeleteAgentPolicyResponse, - Settings, FleetServerPolicy, Installation, Output, DeletePackagePoliciesResponse, } from '../../common'; import { AgentPolicyNameExistsError, HostedAgentPolicyRestrictionRelatedError } from '../errors'; -import { - storedPackagePoliciesToAgentPermissions, - DEFAULT_PERMISSIONS, -} from '../services/package_policies_to_agent_permissions'; import { getPackageInfo } from './epm/packages'; import { getAgentsByKuery } from './agents'; import { packagePolicyService } from './package_policy'; import { outputService } from './output'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; -import { getSettings } from './settings'; import { normalizeKuery, escapeSearchQueryPhrase } from './saved_object'; import { appContextService } from './app_context'; +import { getFullAgentPolicy } from './agent_policies'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; -const MONITORING_DATASETS = [ - 'elastic_agent', - 'elastic_agent.elastic_agent', - 'elastic_agent.apm_server', - 'elastic_agent.filebeat', - 'elastic_agent.fleet_server', - 'elastic_agent.metricbeat', - 'elastic_agent.osquerybeat', - 'elastic_agent.packetbeat', - 'elastic_agent.endpoint_security', - 'elastic_agent.auditbeat', - 'elastic_agent.heartbeat', -]; - class AgentPolicyService { private triggerAgentPolicyUpdatedEvent = async ( soClient: SavedObjectsClientContract, @@ -472,6 +445,38 @@ class AgentPolicyService { return res; } + public async bumpAllAgentPoliciesForOutput( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + outputId: string, + options?: { user?: AuthenticatedUser } + ): Promise<SavedObjectsBulkUpdateResponse<AgentPolicy>> { + const currentPolicies = await soClient.find<AgentPolicySOAttributes>({ + type: SAVED_OBJECT_TYPE, + fields: ['revision', 'data_output_id', 'monitoring_output_id'], + searchFields: ['data_output_id', 'monitoring_output_id'], + search: escapeSearchQueryPhrase(outputId), + }); + const bumpedPolicies = currentPolicies.saved_objects.map((policy) => { + policy.attributes = { + ...policy.attributes, + revision: policy.attributes.revision + 1, + updated_at: new Date().toISOString(), + updated_by: options?.user ? options.user.username : 'system', + }; + return policy; + }); + const res = await soClient.bulkUpdate<AgentPolicySOAttributes>(bumpedPolicies); + + await Promise.all( + currentPolicies.saved_objects.map((policy) => + this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'updated', policy.id) + ) + ); + + return res; + } + public async bumpAllAgentPolicies( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -724,139 +729,7 @@ class AgentPolicyService { id: string, options?: { standalone: boolean } ): Promise<FullAgentPolicy | null> { - let agentPolicy; - const standalone = options?.standalone; - - try { - agentPolicy = await this.get(soClient, id); - } catch (err) { - if (!err.isBoom || err.output.statusCode !== 404) { - throw err; - } - } - - if (!agentPolicy) { - return null; - } - - const defaultOutputId = await outputService.getDefaultOutputId(soClient); - if (!defaultOutputId) { - throw new Error('Default output is not setup'); - } - const defaultOutput = await outputService.get(soClient, defaultOutputId); - - const fullAgentPolicy: FullAgentPolicy = { - id: agentPolicy.id, - outputs: { - // TEMPORARY as we only support a default output - ...[defaultOutput].reduce<FullAgentPolicy['outputs']>( - // eslint-disable-next-line @typescript-eslint/naming-convention - (outputs, { config_yaml, name, type, hosts, ca_sha256, api_key }) => { - const configJs = config_yaml ? safeLoad(config_yaml) : {}; - outputs[name] = { - type, - hosts, - ca_sha256, - api_key, - ...configJs, - }; - - if (options?.standalone) { - delete outputs[name].api_key; - outputs[name].username = 'ES_USERNAME'; - outputs[name].password = 'ES_PASSWORD'; - } - - return outputs; - }, - {} - ), - }, - inputs: storedPackagePoliciesToAgentInputs(agentPolicy.package_policies as PackagePolicy[]), - revision: agentPolicy.revision, - ...(agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 - ? { - agent: { - monitoring: { - namespace: agentPolicy.namespace, - use_output: defaultOutput.name, - enabled: true, - logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), - metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), - }, - }, - } - : { - agent: { - monitoring: { enabled: false, logs: false, metrics: false }, - }, - }), - }; - - const permissions = (await storedPackagePoliciesToAgentPermissions( - soClient, - agentPolicy.package_policies - )) || { _fallback: DEFAULT_PERMISSIONS }; - - permissions._elastic_agent_checks = { - cluster: DEFAULT_PERMISSIONS.cluster, - }; - - // TODO: fetch this from the elastic agent package - const monitoringOutput = fullAgentPolicy.agent?.monitoring.use_output; - const monitoringNamespace = fullAgentPolicy.agent?.monitoring.namespace; - if ( - fullAgentPolicy.agent?.monitoring.enabled && - monitoringNamespace && - monitoringOutput && - fullAgentPolicy.outputs[monitoringOutput]?.type === 'elasticsearch' - ) { - let names: string[] = []; - if (fullAgentPolicy.agent.monitoring.logs) { - names = names.concat( - MONITORING_DATASETS.map((dataset) => `logs-${dataset}-${monitoringNamespace}`) - ); - } - if (fullAgentPolicy.agent.monitoring.metrics) { - names = names.concat( - MONITORING_DATASETS.map((dataset) => `metrics-${dataset}-${monitoringNamespace}`) - ); - } - - permissions._elastic_agent_checks.indices = [ - { - names, - privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, - }, - ]; - } - - // Only add permissions if output.type is "elasticsearch" - fullAgentPolicy.output_permissions = Object.keys(fullAgentPolicy.outputs).reduce< - NonNullable<FullAgentPolicy['output_permissions']> - >((outputPermissions, outputName) => { - const output = fullAgentPolicy.outputs[outputName]; - if (output && output.type === 'elasticsearch') { - outputPermissions[outputName] = permissions; - } - return outputPermissions; - }, {}); - - // only add settings if not in standalone - if (!standalone) { - let settings: Settings; - try { - settings = await getSettings(soClient); - } catch (error) { - throw new Error('Default settings is not setup'); - } - if (settings.fleet_server_hosts && settings.fleet_server_hosts.length) { - fullAgentPolicy.fleet = { - hosts: settings.fleet_server_hosts, - }; - } - } - return fullAgentPolicy; + return getFullAgentPolicy(soClient, id, options); } } diff --git a/x-pack/plugins/fleet/server/services/output.test.ts b/x-pack/plugins/fleet/server/services/output.test.ts index 26e3955607ad..8103794fb080 100644 --- a/x-pack/plugins/fleet/server/services/output.test.ts +++ b/x-pack/plugins/fleet/server/services/output.test.ts @@ -5,8 +5,10 @@ * 2.0. */ -import { outputService } from './output'; +import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import type { OutputSOAttributes } from '../types'; +import { outputService, outputIdToUuid } from './output'; import { appContextService } from './app_context'; jest.mock('./app_context'); @@ -34,7 +36,97 @@ const CONFIG_WITHOUT_ES_HOSTS = { }, }; +function getMockedSoClient() { + const soClient = savedObjectsClientMock.create(); + soClient.get.mockImplementation(async (type: string, id: string) => { + switch (id) { + case outputIdToUuid('output-test'): { + return { + id: outputIdToUuid('output-test'), + type: 'ingest-outputs', + references: [], + attributes: { + output_id: 'output-test', + }, + }; + } + default: + throw new Error('not found'); + } + }); + + return soClient; +} + describe('Output Service', () => { + describe('create', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + soClient.create.mockResolvedValue({ + id: outputIdToUuid('output-test'), + type: 'ingest-output', + attributes: {}, + references: [], + }); + await outputService.create( + soClient, + { + is_default: false, + name: 'Test', + type: 'elasticsearch', + }, + { id: 'output-test' } + ); + + expect(soClient.create).toBeCalled(); + + // ID should always be the same for a predefined id + expect(soClient.create.mock.calls[0][2]?.id).toEqual(outputIdToUuid('output-test')); + expect((soClient.create.mock.calls[0][1] as OutputSOAttributes).output_id).toEqual( + 'output-test' + ); + }); + }); + + describe('get', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + const output = await outputService.get(soClient, 'output-test'); + + expect(soClient.get).toHaveBeenCalledWith('ingest-outputs', outputIdToUuid('output-test')); + + expect(output.id).toEqual('output-test'); + }); + }); + + describe('getDefaultOutputId', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + soClient.find.mockResolvedValue({ + page: 1, + per_page: 100, + total: 1, + saved_objects: [ + { + id: outputIdToUuid('output-test'), + type: 'ingest-outputs', + references: [], + score: 0, + attributes: { + output_id: 'output-test', + is_default: true, + }, + }, + ], + }); + const defaultId = await outputService.getDefaultOutputId(soClient); + + expect(soClient.find).toHaveBeenCalled(); + + expect(defaultId).toEqual('output-test'); + }); + }); + describe('getDefaultESHosts', () => { afterEach(() => { mockedAppContextService.getConfig.mockReset(); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 8c6bc7eca040..5a7ba1e2c122 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { SavedObjectsClientContract } from 'src/core/server'; +import type { SavedObject, SavedObjectsClientContract } from 'src/core/server'; +import uuid from 'uuid/v5'; import type { NewOutput, Output, OutputSOAttributes } from '../types'; import { DEFAULT_OUTPUT, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; @@ -17,8 +18,33 @@ const SAVED_OBJECT_TYPE = OUTPUT_SAVED_OBJECT_TYPE; const DEFAULT_ES_HOSTS = ['http://localhost:9200']; +// differentiate +function isUUID(val: string) { + return ( + typeof val === 'string' && + val.match(/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/) + ); +} + +export function outputIdToUuid(id: string) { + if (isUUID(id)) { + return id; + } + + // UUID v5 need a namespace (uuid.DNS), changing this params will result in loosing the ability to generate predicable uuid + return uuid(id, uuid.DNS); +} + +function outputSavedObjectToOutput(so: SavedObject<OutputSOAttributes>) { + const { output_id: outputId, ...atributes } = so.attributes; + return { + id: outputId ?? so.id, + ...atributes, + }; +} + class OutputService { - public async getDefaultOutput(soClient: SavedObjectsClientContract) { + private async _getDefaultOutputsSO(soClient: SavedObjectsClientContract) { return await soClient.find<OutputSOAttributes>({ type: OUTPUT_SAVED_OBJECT_TYPE, searchFields: ['is_default'], @@ -27,7 +53,7 @@ class OutputService { } public async ensureDefaultOutput(soClient: SavedObjectsClientContract) { - const outputs = await this.getDefaultOutput(soClient); + const outputs = await this._getDefaultOutputsSO(soClient); if (!outputs.saved_objects.length) { const newDefaultOutput = { @@ -39,10 +65,7 @@ class OutputService { return await this.create(soClient, newDefaultOutput); } - return { - id: outputs.saved_objects[0].id, - ...outputs.saved_objects[0].attributes, - }; + return outputSavedObjectToOutput(outputs.saved_objects[0]); } public getDefaultESHosts(): string[] { @@ -60,49 +83,84 @@ class OutputService { } public async getDefaultOutputId(soClient: SavedObjectsClientContract) { - const outputs = await this.getDefaultOutput(soClient); + const outputs = await this._getDefaultOutputsSO(soClient); if (!outputs.saved_objects.length) { return null; } - return outputs.saved_objects[0].id; + return outputSavedObjectToOutput(outputs.saved_objects[0]).id; } public async create( soClient: SavedObjectsClientContract, output: NewOutput, - options?: { id?: string } + options?: { id?: string; overwrite?: boolean } ): Promise<Output> { - const data = { ...output }; + const data: OutputSOAttributes = { ...output }; + + // ensure only default output exists + if (data.is_default) { + const defaultOuput = await this.getDefaultOutputId(soClient); + if (defaultOuput) { + throw new Error(`A default output already exists (${defaultOuput})`); + } + } if (data.hosts) { data.hosts = data.hosts.map(normalizeHostsForAgents); } - const newSo = await soClient.create<OutputSOAttributes>( - SAVED_OBJECT_TYPE, - data as Output, - options - ); + if (options?.id) { + data.output_id = options?.id; + } + + const newSo = await soClient.create<OutputSOAttributes>(SAVED_OBJECT_TYPE, data, { + ...options, + id: options?.id ? outputIdToUuid(options.id) : undefined, + }); return { - id: newSo.id, + id: options?.id ?? newSo.id, ...newSo.attributes, }; } + public async bulkGet( + soClient: SavedObjectsClientContract, + ids: string[], + { ignoreNotFound = false } = { ignoreNotFound: true } + ) { + const res = await soClient.bulkGet<OutputSOAttributes>( + ids.map((id) => ({ id: outputIdToUuid(id), type: SAVED_OBJECT_TYPE })) + ); + + return res.saved_objects + .map((so) => { + if (so.error) { + if (!ignoreNotFound || so.error.statusCode !== 404) { + throw so.error; + } + return undefined; + } + + return outputSavedObjectToOutput(so); + }) + .filter((output): output is Output => typeof output !== 'undefined'); + } + public async get(soClient: SavedObjectsClientContract, id: string): Promise<Output> { - const outputSO = await soClient.get<OutputSOAttributes>(SAVED_OBJECT_TYPE, id); + const outputSO = await soClient.get<OutputSOAttributes>(SAVED_OBJECT_TYPE, outputIdToUuid(id)); if (outputSO.error) { throw new Error(outputSO.error.message); } - return { - id: outputSO.id, - ...outputSO.attributes, - }; + return outputSavedObjectToOutput(outputSO); + } + + public async delete(soClient: SavedObjectsClientContract, id: string) { + return soClient.delete(SAVED_OBJECT_TYPE, outputIdToUuid(id)); } public async update(soClient: SavedObjectsClientContract, id: string, data: Partial<Output>) { @@ -111,8 +169,11 @@ class OutputService { if (updateData.hosts) { updateData.hosts = updateData.hosts.map(normalizeHostsForAgents); } - - const outputSO = await soClient.update<OutputSOAttributes>(SAVED_OBJECT_TYPE, id, updateData); + const outputSO = await soClient.update<OutputSOAttributes>( + SAVED_OBJECT_TYPE, + outputIdToUuid(id), + updateData + ); if (outputSO.error) { throw new Error(outputSO.error.message); @@ -127,12 +188,7 @@ class OutputService { }); return { - items: outputs.saved_objects.map<Output>((outputSO) => { - return { - id: outputSO.id, - ...outputSO.attributes, - }; - }), + items: outputs.saved_objects.map<Output>(outputSavedObjectToOutput), total: outputs.total, page: 1, perPage: 1000, diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index 86fdd2f0aa80..43887bc2787f 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -9,7 +9,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/serve import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; -import type { PreconfiguredAgentPolicy } from '../../common/types'; +import type { PreconfiguredAgentPolicy, PreconfiguredOutput } from '../../common/types'; import type { AgentPolicy, NewPackagePolicy, Output } from '../types'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../constants'; @@ -19,9 +19,15 @@ import * as agentPolicy from './agent_policy'; import { ensurePreconfiguredPackagesAndPolicies, comparePreconfiguredPolicyToCurrent, + ensurePreconfiguredOutputs, + cleanPreconfiguredOutputs, } from './preconfiguration'; +import { outputService } from './output'; jest.mock('./agent_policy_update'); +jest.mock('./output'); + +const mockedOutputService = outputService as jest.Mocked<typeof outputService>; const mockInstalledPackages = new Map(); const mockConfiguredPolicies = new Map(); @@ -156,12 +162,17 @@ jest.mock('./app_context', () => ({ })); const spyAgentPolicyServiceUpdate = jest.spyOn(agentPolicy.agentPolicyService, 'update'); +const spyAgentPolicyServicBumpAllAgentPoliciesForOutput = jest.spyOn( + agentPolicy.agentPolicyService, + 'bumpAllAgentPoliciesForOutput' +); describe('policy preconfiguration', () => { beforeEach(() => { mockInstalledPackages.clear(); mockConfiguredPolicies.clear(); spyAgentPolicyServiceUpdate.mockClear(); + spyAgentPolicyServicBumpAllAgentPoliciesForOutput.mockClear(); }); it('should perform a no-op when passed no policies or packages', async () => { @@ -480,3 +491,168 @@ describe('comparePreconfiguredPolicyToCurrent', () => { expect(hasChanged).toBe(false); }); }); + +describe('output preconfiguration', () => { + beforeEach(() => { + mockedOutputService.create.mockReset(); + mockedOutputService.update.mockReset(); + mockedOutputService.getDefaultESHosts.mockReturnValue(['http://default-es:9200']); + mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise<Output[]> => { + return [ + { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es.co:80'], + is_preconfigured: true, + }, + ]; + }); + }); + + it('should create preconfigured output that does not exists', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: false, + hosts: ['http://test.fr'], + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should set default hosts if hosts is not set output that does not exists', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: false, + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.create.mock.calls[0][1].hosts).toEqual(['http://default-es:9200']); + }); + + it('should update output if preconfigured output exists and changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://newhostichanged.co:9201'], // field that changed + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + const SCENARIOS: Array<{ name: string; data: PreconfiguredOutput }> = [ + { + name: 'no changes', + data: { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:80'], + }, + }, + { + name: 'hosts without port', + data: { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co'], + }, + }, + ]; + SCENARIOS.forEach((scenario) => { + const { data, name } = scenario; + it(`should do nothing if preconfigured output exists and did not changed (${name})`, async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [data]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + }); + }); + + it('should not delete non deleted preconfigured output', async () => { + const soClient = savedObjectsClientMock.create(); + mockedOutputService.list.mockResolvedValue({ + items: [ + { id: 'output1', is_preconfigured: true } as Output, + { id: 'output2', is_preconfigured: true } as Output, + ], + page: 1, + perPage: 10000, + total: 1, + }); + await cleanPreconfiguredOutputs(soClient, [ + { + id: 'output1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + { + id: 'output2', + is_default: false, + name: 'Output 2', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + ]); + + expect(mockedOutputService.delete).not.toBeCalled(); + }); + + it('should delete deleted preconfigured output', async () => { + const soClient = savedObjectsClientMock.create(); + mockedOutputService.list.mockResolvedValue({ + items: [ + { id: 'output1', is_preconfigured: true } as Output, + { id: 'output2', is_preconfigured: true } as Output, + ], + page: 1, + perPage: 10000, + total: 1, + }); + await cleanPreconfiguredOutputs(soClient, [ + { + id: 'output1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + ]); + + expect(mockedOutputService.delete).toBeCalled(); + expect(mockedOutputService.delete).toBeCalledTimes(1); + expect(mockedOutputService.delete.mock.calls[0][1]).toEqual('output2'); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 37ed98a6f4aa..30c5c27c6891 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -8,6 +8,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { groupBy, omit, pick, isEqual } from 'lodash'; +import { safeDump } from 'js-yaml'; import type { NewPackagePolicy, @@ -17,16 +18,15 @@ import type { PreconfiguredAgentPolicy, PreconfiguredPackage, PreconfigurationError, + PreconfiguredOutput, } from '../../common'; -import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common'; - +import { AGENT_POLICY_SAVED_OBJECT_TYPE, normalizeHostsForAgents } from '../../common'; import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, PRECONFIGURATION_LATEST_KEYWORD, } from '../constants'; import { escapeSearchQueryPhrase } from './saved_object'; - import { pkgToPkgKey } from './epm/registry'; import { getInstallation, getPackageInfo } from './epm/packages'; import { ensurePackagesCompletedInstall } from './epm/packages/install'; @@ -35,6 +35,7 @@ import { agentPolicyService, addPackageToAgentPolicy } from './agent_policy'; import type { InputsOverride } from './package_policy'; import { overridePackageInputs } from './package_policy'; import { appContextService } from './app_context'; +import { outputService } from './output'; interface PreconfigurationResult { policies: Array<{ id: string; updated_at: string }>; @@ -42,6 +43,89 @@ interface PreconfigurationResult { nonFatalErrors: PreconfigurationError[]; } +function isPreconfiguredOutputDifferentFromCurrent( + existingOutput: Output, + preconfiguredOutput: Partial<Output> +): boolean { + return ( + existingOutput.is_default !== preconfiguredOutput.is_default || + existingOutput.name !== preconfiguredOutput.name || + existingOutput.type !== preconfiguredOutput.type || + (preconfiguredOutput.hosts && + !isEqual( + existingOutput.hosts?.map(normalizeHostsForAgents), + preconfiguredOutput.hosts.map(normalizeHostsForAgents) + )) || + existingOutput.ca_sha256 !== preconfiguredOutput.ca_sha256 || + existingOutput.config_yaml !== preconfiguredOutput.config_yaml + ); +} + +export async function ensurePreconfiguredOutputs( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + outputs: PreconfiguredOutput[] +) { + if (outputs.length === 0) { + return; + } + + const existingOutputs = await outputService.bulkGet( + soClient, + outputs.map(({ id }) => id), + { ignoreNotFound: true } + ); + + await Promise.all( + outputs.map(async (output) => { + const existingOutput = existingOutputs.find((o) => o.id === output.id); + + const { id, config, ...outputData } = output; + + const configYaml = config ? safeDump(config) : undefined; + + const data = { + ...outputData, + config_yaml: configYaml, + is_preconfigured: true, + }; + + if (!data.hosts || data.hosts.length === 0) { + data.hosts = outputService.getDefaultESHosts(); + } + + if (!existingOutput) { + await outputService.create(soClient, data, { id, overwrite: true }); + } else if (isPreconfiguredOutputDifferentFromCurrent(existingOutput, data)) { + await outputService.update(soClient, id, data); + // Bump revision of all policies using that output + if (outputData.is_default) { + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); + } else { + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id); + } + } + }) + ); +} + +export async function cleanPreconfiguredOutputs( + soClient: SavedObjectsClientContract, + outputs: PreconfiguredOutput[] +) { + const existingPreconfiguredOutput = (await outputService.list(soClient)).items.filter( + (o) => o.is_preconfigured === true + ); + const logger = appContextService.getLogger(); + + for (const output of existingPreconfiguredOutput) { + if (!outputs.find(({ id }) => output.id === id)) { + logger.info(`Deleting preconfigured output ${output.id}`); + await outputService.delete(soClient, output.id); + } + } +} + export async function ensurePreconfiguredPackagesAndPolicies( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -224,7 +308,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( } // Add the is_managed flag after configuring package policies to avoid errors if (shouldAddIsManagedFlag) { - agentPolicyService.update(soClient, esClient, policy!.id, { is_managed: true }); + await agentPolicyService.update(soClient, esClient, policy!.id, { is_managed: true }); } } } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 1f3c3c5082b3..8c49bffdbf25 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -15,7 +15,11 @@ import { SO_SEARCH_LIMIT, DEFAULT_PACKAGES } from '../constants'; import { appContextService } from './app_context'; import { agentPolicyService } from './agent_policy'; -import { ensurePreconfiguredPackagesAndPolicies } from './preconfiguration'; +import { + cleanPreconfiguredOutputs, + ensurePreconfiguredOutputs, + ensurePreconfiguredPackagesAndPolicies, +} from './preconfiguration'; import { outputService } from './output'; import { generateEnrollmentAPIKey, hasEnrollementAPIKeysForPolicy } from './api_keys'; @@ -45,23 +49,27 @@ async function createSetupSideEffects( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient ): Promise<SetupStatus> { - const [defaultOutput] = await Promise.all([ - outputService.ensureDefaultOutput(soClient), + const { + agentPolicies: policiesOrUndefined, + packages: packagesOrUndefined, + outputs: outputsOrUndefined, + } = appContextService.getConfig() ?? {}; + + const policies = policiesOrUndefined ?? []; + let packages = packagesOrUndefined ?? []; + + await Promise.all([ + ensurePreconfiguredOutputs(soClient, esClient, outputsOrUndefined ?? []), settingsService.settingsSetup(soClient), ]); + const defaultOutput = await outputService.ensureDefaultOutput(soClient); + await awaitIfFleetServerSetupPending(); if (appContextService.getConfig()?.agentIdVerificationEnabled) { await ensureFleetGlobalEsAssets(soClient, esClient); } - const { agentPolicies: policiesOrUndefined, packages: packagesOrUndefined } = - appContextService.getConfig() ?? {}; - - const policies = policiesOrUndefined ?? []; - - let packages = packagesOrUndefined ?? []; - // Ensure that required packages are always installed even if they're left out of the config const preconfiguredPackageNames = new Set(packages.map((pkg) => pkg.name)); @@ -90,6 +98,8 @@ async function createSetupSideEffects( defaultOutput ); + await cleanPreconfiguredOutputs(soClient, outputsOrUndefined ?? []); + await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); await ensureAgentActionPolicyChangeExists(soClient, esClient); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index f686b969fd03..63e6c277ed71 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -27,6 +27,7 @@ export { PackagePolicySOAttributes, FullAgentPolicyInput, FullAgentPolicy, + FullAgentPolicyOutput, AgentPolicy, AgentPolicySOAttributes, NewAgentPolicy, diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts new file mode 100644 index 000000000000..eb349e0d0f82 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PreconfiguredOutputsSchema, PreconfiguredAgentPoliciesSchema } from './preconfiguration'; + +describe('Test preconfiguration schema', () => { + describe('PreconfiguredOutputsSchema', () => { + it('should not allow multiple default output', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: true, + }, + { + id: 'output-2', + name: 'Output 2', + type: 'elasticsearch', + is_default: true, + }, + ]); + }).toThrowError('preconfigured outputs need to have only one default output.'); + }); + it('should not allow multiple output with same ids', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'nonuniqueid', + name: 'Output 1', + type: 'elasticsearch', + }, + { + id: 'nonuniqueid', + name: 'Output 2', + type: 'elasticsearch', + }, + ]); + }).toThrowError('preconfigured outputs need to have unique ids.'); + }); + it('should not allow multiple output with same names', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'output-1', + name: 'nonuniquename', + type: 'elasticsearch', + }, + { + id: 'output-2', + name: 'nonuniquename', + type: 'elasticsearch', + }, + ]); + }).toThrowError('preconfigured outputs need to have unique names.'); + }); + }); + + describe('PreconfiguredAgentPoliciesSchema', () => { + it('should not allow multiple outputs in one policy', () => { + expect(() => { + PreconfiguredAgentPoliciesSchema.validate([ + { + id: 'policy-1', + name: 'Policy 1', + package_policies: [], + data_output_id: 'test1', + monitoring_output_id: 'test2', + }, + ]); + }).toThrowError( + '[0]: Currently Fleet only support one output per agent policy data_output_id should be the same as monitoring_output_id.' + ); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts index 4ea9f086bda6..b65fa122911d 100644 --- a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts @@ -14,6 +14,8 @@ import { DEFAULT_FLEET_SERVER_AGENT_POLICY, DEFAULT_PACKAGES, } from '../../constants'; +import type { PreconfiguredOutput } from '../../../common'; +import { outputType } from '../../../common'; import { AgentPolicyBaseSchema } from './agent_policy'; import { NamespaceSchema } from './package_policy'; @@ -47,47 +49,94 @@ export const PreconfiguredPackagesSchema = schema.arrayOf( } ); -export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( +function validatePreconfiguredOutputs(outputs: PreconfiguredOutput[]) { + const acc = { names: new Set(), ids: new Set(), is_default: false }; + + for (const output of outputs) { + if (acc.names.has(output.name)) { + return 'preconfigured outputs need to have unique names.'; + } + if (acc.ids.has(output.id)) { + return 'preconfigured outputs need to have unique ids.'; + } + if (acc.is_default && output.is_default) { + return 'preconfigured outputs need to have only one default output.'; + } + + acc.ids.add(output.id); + acc.names.add(output.name); + acc.is_default = acc.is_default || output.is_default; + } +} + +export const PreconfiguredOutputsSchema = schema.arrayOf( schema.object({ - ...AgentPolicyBaseSchema, - namespace: schema.maybe(NamespaceSchema), - id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), - is_default: schema.maybe(schema.boolean()), - is_default_fleet_server: schema.maybe(schema.boolean()), - package_policies: schema.arrayOf( - schema.object({ - name: schema.string(), - package: schema.object({ - name: schema.string(), - }), - description: schema.maybe(schema.string()), - namespace: schema.maybe(NamespaceSchema), - inputs: schema.maybe( - schema.arrayOf( - schema.object({ - type: schema.string(), - enabled: schema.maybe(schema.boolean()), - keep_enabled: schema.maybe(schema.boolean()), - vars: varsSchema, - streams: schema.maybe( - schema.arrayOf( - schema.object({ - data_stream: schema.object({ - type: schema.maybe(schema.string()), - dataset: schema.string(), - }), - enabled: schema.maybe(schema.boolean()), - keep_enabled: schema.maybe(schema.boolean()), - vars: varsSchema, - }) - ) - ), - }) - ) - ), - }) - ), + id: schema.string(), + is_default: schema.boolean({ defaultValue: false }), + name: schema.string(), + type: schema.oneOf([schema.literal(outputType.Elasticsearch)]), + hosts: schema.maybe(schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }))), + ca_sha256: schema.maybe(schema.string()), + config: schema.maybe(schema.object({}, { unknowns: 'allow' })), }), + { + defaultValue: [], + validate: validatePreconfiguredOutputs, + } +); + +export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( + schema.object( + { + ...AgentPolicyBaseSchema, + namespace: schema.maybe(NamespaceSchema), + id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), + is_default: schema.maybe(schema.boolean()), + is_default_fleet_server: schema.maybe(schema.boolean()), + data_output_id: schema.maybe(schema.string()), + monitoring_output_id: schema.maybe(schema.string()), + package_policies: schema.arrayOf( + schema.object({ + name: schema.string(), + package: schema.object({ + name: schema.string(), + }), + description: schema.maybe(schema.string()), + namespace: schema.maybe(NamespaceSchema), + inputs: schema.maybe( + schema.arrayOf( + schema.object({ + type: schema.string(), + enabled: schema.maybe(schema.boolean()), + keep_enabled: schema.maybe(schema.boolean()), + vars: varsSchema, + streams: schema.maybe( + schema.arrayOf( + schema.object({ + data_stream: schema.object({ + type: schema.maybe(schema.string()), + dataset: schema.string(), + }), + enabled: schema.maybe(schema.boolean()), + keep_enabled: schema.maybe(schema.boolean()), + vars: varsSchema, + }) + ) + ), + }) + ) + ), + }) + ), + }, + { + validate: (policy) => { + if (policy.data_output_id !== policy.monitoring_output_id) { + return 'Currently Fleet only support one output per agent policy data_output_id should be the same as monitoring_output_id.'; + } + }, + } + ), { defaultValue: [DEFAULT_AGENT_POLICY, DEFAULT_FLEET_SERVER_AGENT_POLICY], } From 65ec86da66919cc4d0c318d1eef3a2f2fb680f4f Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet <pierre.gayvallet@gmail.com> Date: Tue, 21 Sep 2021 20:19:34 +0200 Subject: [PATCH 34/36] handle source index without any mappings (#112664) --- .../migrations/core/migration_context.test.ts | 14 ++++++++++++++ .../migrations/core/migration_context.ts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/server/saved_objects/migrations/core/migration_context.test.ts b/src/core/server/saved_objects/migrations/core/migration_context.test.ts index 240b41266abb..27aae5968ba8 100644 --- a/src/core/server/saved_objects/migrations/core/migration_context.test.ts +++ b/src/core/server/saved_objects/migrations/core/migration_context.test.ts @@ -74,4 +74,18 @@ describe('disableUnknownTypeMappingFields', () => { }, }); }); + + it('does not fail if the source mapping does not have `properties` defined', () => { + const missingPropertiesMappings = { + ...sourceMappings, + properties: undefined, + }; + const result = disableUnknownTypeMappingFields( + activeMappings, + // @ts-expect-error `properties` should not be undefined + missingPropertiesMappings + ); + + expect(Object.keys(result.properties)).toEqual(['known_type']); + }); }); diff --git a/src/core/server/saved_objects/migrations/core/migration_context.ts b/src/core/server/saved_objects/migrations/core/migration_context.ts index d7f7aff45a47..96c47bcf38d0 100644 --- a/src/core/server/saved_objects/migrations/core/migration_context.ts +++ b/src/core/server/saved_objects/migrations/core/migration_context.ts @@ -154,7 +154,7 @@ export function disableUnknownTypeMappingFields( ): IndexMapping { const targetTypes = Object.keys(activeMappings.properties); - const disabledTypesProperties = Object.keys(sourceMappings.properties) + const disabledTypesProperties = Object.keys(sourceMappings.properties ?? {}) .filter((sourceType) => { const isObjectType = 'properties' in sourceMappings.properties[sourceType]; // Only Object/Nested datatypes can be excluded from the field count by From ce6ed0875a0875cab0d814a828fd6ab4e742b2c4 Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Tue, 21 Sep 2021 12:24:39 -0700 Subject: [PATCH 35/36] skip flaky suite (#111922) Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- test/functional/apps/discover/_runtime_fields_editor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index 642743d3a037..4757807cb7ac 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -31,7 +31,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.save(); }; - describe('discover integration with runtime fields editor', function describeIndexTests() { + // Failing: https://github.com/elastic/kibana/issues/111922 + describe.skip('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); From 25bf7950999ae025a9275155c1641e3f007990d5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens <jloleysens@gmail.com> Date: Tue, 21 Sep 2021 21:42:32 +0200 Subject: [PATCH 36/36] [Reporting/Discover/Search Sessions] Only use relative time filter when generating share data (#112588) * only use relative time filter when generating share data * Added comment on absolute time filter. Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> * improve discover search session relative time range test * update discover tests and types for passing in data plugin to getSharingData function * updated reporting to pass in data plugin to getSharingData, also updates jest tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> Co-authored-by: Anton Dosov <anton.dosov@elastic.co> --- .../components/top_nav/get_top_nav_links.ts | 2 +- .../apps/main/utils/get_sharing_data.test.ts | 37 ++++++++------ .../apps/main/utils/get_sharing_data.ts | 11 +++-- .../apps/main/utils/update_search_source.ts | 6 +-- .../get_csv_panel_action.test.ts | 49 +++++++++++-------- .../panel_actions/get_csv_panel_action.tsx | 17 ++++--- x-pack/plugins/reporting/public/plugin.ts | 7 ++- .../search_sessions_management_page.ts | 1 + .../tests/apps/discover/async_search.ts | 13 ++++- 9 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts index 58a7242974ba..ba4cd8c3cd52 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts @@ -112,7 +112,7 @@ export const getTopNavLinks = ({ const sharingData = await getSharingData( searchSource, state.appStateContainer.getState(), - services.uiSettings + services ); services.share.toggleShareContextMenu({ diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts index 25d0ca5d66eb..e7205c3f9bc6 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts @@ -7,32 +7,37 @@ */ import { Capabilities, IUiSettingsClient } from 'kibana/public'; -import { IndexPattern } from 'src/plugins/data/public'; +import type { IndexPattern } from 'src/plugins/data/public'; +import type { DiscoverServices } from '../../../../build_services'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; import { createSearchSourceMock } from '../../../../../../data/common/search/search_source/mocks'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; import { getSharingData, showPublicUrlSwitch } from './get_sharing_data'; describe('getSharingData', () => { - let mockConfig: IUiSettingsClient; + let services: DiscoverServices; beforeEach(() => { - mockConfig = { - get: (key: string) => { - if (key === SORT_DEFAULT_ORDER_SETTING) { - return 'desc'; - } - if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + services = { + data: dataPluginMock.createStartContract(), + uiSettings: { + get: (key: string) => { + if (key === SORT_DEFAULT_ORDER_SETTING) { + return 'desc'; + } + if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + return false; + } return false; - } - return false; + }, }, - } as unknown as IUiSettingsClient; + } as DiscoverServices; }); test('returns valid data for sharing', async () => { const searchSourceMock = createSearchSourceMock({ index: indexPatternMock }); - const result = await getSharingData(searchSourceMock, { columns: [] }, mockConfig); + const result = await getSharingData(searchSourceMock, { columns: [] }, services); expect(result).toMatchInlineSnapshot(` Object { "columns": Array [], @@ -53,7 +58,7 @@ describe('getSharingData', () => { const result = await getSharingData( searchSourceMock, { columns: ['column_a', 'column_b'] }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { @@ -90,7 +95,7 @@ describe('getSharingData', () => { 'cool-field-6', ], }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { @@ -116,7 +121,7 @@ describe('getSharingData', () => { }); test('fields conditionally do not have prepended timeField', async () => { - mockConfig = { + services.uiSettings = { get: (key: string) => { if (key === DOC_HIDE_TIME_COLUMN_SETTING) { return true; @@ -141,7 +146,7 @@ describe('getSharingData', () => { 'cool-field-6', ], }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts index 65001f49f4d6..420ff0fa11ee 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import type { Capabilities, IUiSettingsClient } from 'kibana/public'; -import { ISearchSource } from '../../../../../../data/common'; +import type { Capabilities } from 'kibana/public'; +import type { IUiSettingsClient } from 'src/core/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { ISearchSource } from 'src/plugins/data/common'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import type { SavedSearch, SortOrder } from '../../../../saved_searches/types'; import { getSortForSearchSource } from '../components/doc_table'; @@ -19,8 +21,9 @@ import { AppState } from '../services/discover_state'; export async function getSharingData( currentSearchSource: ISearchSource, state: AppState | SavedSearch, - config: IUiSettingsClient + services: { uiSettings: IUiSettingsClient; data: DataPublicPluginStart } ) { + const { uiSettings: config, data } = services; const searchSource = currentSearchSource.createCopy(); const index = searchSource.getField('index')!; @@ -28,6 +31,8 @@ export async function getSharingData( 'sort', getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING)) ); + // When sharing externally we preserve relative time values + searchSource.setField('filter', data.query.timefilter.timefilter.createRelativeFilter(index)); searchSource.removeField('highlight'); searchSource.removeField('highlightAll'); searchSource.removeField('aggs'); diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts index 74e63c399743..5a4a543a1d5c 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts @@ -54,10 +54,8 @@ export function updateSearchSource( // this is not the default index pattern, it determines that it's not of type rollup if (indexPatternsUtils.isDefault(indexPattern)) { - searchSource.setField( - 'filter', - data.query.timefilter.timefilter.createRelativeFilter(indexPattern) - ); + // Set the date range filter fields from timeFilter using the absolute format. Search sessions requires that it be converted from a relative range + searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern)); } if (useNewFieldsApi) { diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index ceb3ed180cd3..bedf310725ae 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -11,8 +11,10 @@ import { CoreStart } from 'src/core/public'; import type { SearchSource } from 'src/plugins/data/common'; import type { SavedSearch } from 'src/plugins/discover/public'; import { coreMock } from '../../../../../src/core/public/mocks'; +import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; import type { ILicense, LicensingPluginSetup } from '../../../licensing/public'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import type { ReportingPublicPluginStartDendencies } from '../plugin'; import type { ActionContext } from './get_csv_panel_action'; import { ReportingCsvPanelAction } from './get_csv_panel_action'; @@ -25,8 +27,8 @@ describe('GetCsvReportPanelAction', () => { let context: ActionContext; let mockLicense$: (state?: LicenseResults) => Rx.Observable<ILicense>; let mockSearchSource: SearchSource; - let mockStartServicesPayload: [CoreStart, object, unknown]; - let mockStartServices$: Rx.Subject<typeof mockStartServicesPayload>; + let mockStartServicesPayload: [CoreStart, ReportingPublicPluginStartDendencies, unknown]; + let mockStartServices$: Rx.Observable<typeof mockStartServicesPayload>; beforeAll(() => { if (typeof window.URL.revokeObjectURL === 'undefined') { @@ -48,14 +50,17 @@ describe('GetCsvReportPanelAction', () => { }) as unknown as LicensingPluginSetup['license$']; }; - mockStartServices$ = new Rx.Subject<[CoreStart, object, unknown]>(); mockStartServicesPayload = [ { + ...core, application: { capabilities: { dashboard: { downloadCsv: true } } }, } as unknown as CoreStart, - {}, + { + data: dataPluginMock.createStartContract(), + } as ReportingPublicPluginStartDendencies, null, ]; + mockStartServices$ = Rx.from(Promise.resolve(mockStartServicesPayload)); mockSearchSource = { createCopy: () => mockSearchSource, @@ -93,7 +98,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -130,7 +135,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -153,7 +158,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -169,7 +174,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -187,7 +192,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -204,14 +209,13 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); - + await mockStartServices$.pipe(first()).toPromise(); await licenseMock$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(false); }); - it('sets a display and icon type', () => { + it('sets a display and icon type', async () => { const panel = new ReportingCsvPanelAction({ core, apiClient, @@ -220,7 +224,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); expect(panel.getIconType()).toMatchInlineSnapshot(`"document"`); expect(panel.getDisplayName()).toMatchInlineSnapshot(`"Download CSV"`); @@ -228,25 +232,28 @@ describe('GetCsvReportPanelAction', () => { describe('Application UI Capabilities', () => { it(`doesn't allow downloads when UI capability is not enabled`, async () => { + mockStartServicesPayload = [ + { application: { capabilities: {} } } as unknown as CoreStart, + { + data: dataPluginMock.createStartContract(), + } as ReportingPublicPluginStartDendencies, + null, + ]; + const startServices$ = Rx.from(Promise.resolve(mockStartServicesPayload)); const plugin = new ReportingCsvPanelAction({ core, apiClient, license$: mockLicense$(), - startServices$: mockStartServices$, + startServices$, usesUiCapabilities: true, }); - mockStartServices$.next([ - { application: { capabilities: {} } } as unknown as CoreStart, - {}, - null, - ]); + await startServices$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(false); }); it(`allows downloads when license is valid and UI capability is enabled`, async () => { - mockStartServices$ = new Rx.Subject(); const plugin = new ReportingCsvPanelAction({ core, apiClient, @@ -255,7 +262,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(true); }); diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index eb14e3216086..ef32e6474176 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -7,7 +7,8 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; -import type { CoreSetup, IUiSettingsClient, NotificationsSetup } from 'src/core/public'; +import { first } from 'rxjs/operators'; +import type { CoreSetup, NotificationsSetup } from 'src/core/public'; import { CoreStart } from 'src/core/public'; import type { ISearchEmbeddable, SavedSearch } from '../../../../../src/plugins/discover/public'; import { @@ -22,6 +23,7 @@ import type { LicensingPluginSetup } from '../../../licensing/public'; import { CSV_REPORTING_ACTION } from '../../common/constants'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import type { ReportingPublicPluginStartDendencies } from '../plugin'; function isSavedSearchEmbeddable( embeddable: IEmbeddable | ISearchEmbeddable @@ -36,7 +38,7 @@ export interface ActionContext { interface Params { apiClient: ReportingAPIClient; core: CoreSetup; - startServices$: Rx.Observable<[CoreStart, object, unknown]>; + startServices$: Rx.Observable<[CoreStart, ReportingPublicPluginStartDendencies, unknown]>; license$: LicensingPluginSetup['license$']; usesUiCapabilities: boolean; } @@ -47,16 +49,16 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> public readonly id = CSV_REPORTING_ACTION; private licenseHasDownloadCsv: boolean = false; private capabilityHasDownloadCsv: boolean = false; - private uiSettings: IUiSettingsClient; private notifications: NotificationsSetup; private apiClient: ReportingAPIClient; + private startServices$: Params['startServices$']; constructor({ core, startServices$, license$, usesUiCapabilities, apiClient }: Params) { this.isDownloading = false; - this.uiSettings = core.uiSettings; this.notifications = core.notifications; this.apiClient = apiClient; + this.startServices$ = startServices$; license$.subscribe((license) => { const results = license.check('reporting', 'basic'); @@ -65,7 +67,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> }); if (usesUiCapabilities) { - startServices$.subscribe(([{ application }]) => { + this.startServices$.subscribe(([{ application }]) => { this.capabilityHasDownloadCsv = application.capabilities.dashboard?.downloadCsv === true; }); } else { @@ -84,11 +86,12 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> } public async getSearchSource(savedSearch: SavedSearch, embeddable: ISearchEmbeddable) { + const [{ uiSettings }, { data }] = await this.startServices$.pipe(first()).toPromise(); const { getSharingData } = await loadSharingDataHelpers(); return await getSharingData( savedSearch.searchSource, - savedSearch, // TODO: get unsaved state (using embeddale.searchScope): https://github.com/elastic/kibana/issues/43977 - this.uiSettings + savedSearch, // TODO: get unsaved state (using embeddable.searchScope): https://github.com/elastic/kibana/issues/43977 + { uiSettings, data } ); } diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index 28226751975a..7fd6047470a0 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; import { CoreSetup, CoreStart, @@ -77,6 +78,7 @@ export interface ReportingPublicPluginSetupDendencies { export interface ReportingPublicPluginStartDendencies { home: HomePublicPluginStart; + data: DataPublicPluginStart; management: ManagementStart; licensing: LicensingPluginStart; uiActions: UiActionsStart; @@ -134,7 +136,10 @@ export class ReportingPublicPlugin return this.contract; } - public setup(core: CoreSetup, setupDeps: ReportingPublicPluginSetupDendencies) { + public setup( + core: CoreSetup<ReportingPublicPluginStartDendencies>, + setupDeps: ReportingPublicPluginSetupDendencies + ) { const { getStartServices, uiSettings } = core; const { home, diff --git a/x-pack/test/functional/page_objects/search_sessions_management_page.ts b/x-pack/test/functional/page_objects/search_sessions_management_page.ts index 86391b568fdf..15c87ea45042 100644 --- a/x-pack/test/functional/page_objects/search_sessions_management_page.ts +++ b/x-pack/test/functional/page_objects/search_sessions_management_page.ts @@ -38,6 +38,7 @@ export function SearchSessionsPageProvider({ getService, getPageObjects }: FtrPr mainUrl: $.findTestSubject('sessionManagementNameCol').text(), created: $.findTestSubject('sessionManagementCreatedCol').text(), expires: $.findTestSubject('sessionManagementExpiresCol').text(), + searchesCount: Number($.findTestSubject('sessionManagementNumSearchesCol').text()), app: $.findTestSubject('sessionManagementAppIcon').attr('data-test-app-id'), view: async () => { log.debug('management ui: view the session'); diff --git a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts index 3f02e6405632..cd13f71cf1bb 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts @@ -26,6 +26,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const searchSessions = getService('searchSessions'); const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); + const toasts = getService('toasts'); describe('discover async search', () => { before(async () => { @@ -112,12 +113,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // load URL to restore a saved session await PageObjects.searchSessionsManagement.goTo(); - const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + const searchSessionListBeforeRestore = await PageObjects.searchSessionsManagement.getList(); + const searchesCountBeforeRestore = searchSessionListBeforeRestore[0].searchesCount; // navigate to Discover - await searchSessionList[0].view(); + await searchSessionListBeforeRestore[0].view(); await PageObjects.header.waitUntilLoadingHasFinished(); await searchSessions.expectState('restored'); expect(await PageObjects.discover.hasNoResults()).to.be(true); + expect(await toasts.getToastCount()).to.be(0); // no session restoration related warnings + + await PageObjects.searchSessionsManagement.goTo(); + const searchSessionListAfterRestore = await PageObjects.searchSessionsManagement.getList(); + const searchesCountAfterRestore = searchSessionListAfterRestore[0].searchesCount; + + expect(searchesCountBeforeRestore).to.be(searchesCountAfterRestore); // no new searches started during restore }); });