From 7dc9fce0c8e6cc6c22027693a0f542f37e30c9d5 Mon Sep 17 00:00:00 2001
From: Alison Goryachev
Date: Mon, 15 Mar 2021 20:20:58 -0400
Subject: [PATCH 01/24] [Upgrade Assistant] Add support for deprecated index
settings (#93293)
---
.../public/doc_links/doc_links_service.ts | 3 +-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
.../upgrade_assistant/common/constants.ts | 12 +
.../plugins/upgrade_assistant/common/types.ts | 21 +-
.../public/application/app.tsx | 4 +-
.../public/application/app_context.tsx | 3 +-
.../public/application/components/tabs.tsx | 6 +-
.../__snapshots__/checkup_tab.test.tsx.snap | 442 +++++++++---------
.../components/tabs/checkup/checkup_tab.tsx | 4 +-
.../tabs/checkup/deprecations/cell.tsx | 9 +
.../checkup/deprecations/grouped.test.tsx | 1 +
.../tabs/checkup/deprecations/grouped.tsx | 5 +-
.../deprecations/index_settings/button.tsx | 54 +++
.../deprecations/index_settings/index.ts | 8 +
.../remove_settings_provider.tsx | 137 ++++++
.../checkup/deprecations/index_table.test.tsx | 1 +
.../tabs/checkup/deprecations/index_table.tsx | 51 +-
.../tabs/checkup/deprecations/list.test.tsx | 2 +
.../tabs/checkup/deprecations/list.tsx | 11 +-
.../checkup/deprecations/reindex/button.tsx | 3 +-
.../checklist_step.test.tsx.snap | 4 +-
.../__snapshots__/warning_step.test.tsx.snap | 2 +-
.../deprecations/reindex/flyout/_index.scss | 1 -
.../reindex/flyout/_warnings_step.scss | 4 -
.../reindex/flyout/checklist_step.test.tsx | 4 +-
.../reindex/flyout/warning_step.test.tsx | 43 +-
.../reindex/flyout/warning_step_checkbox.tsx | 161 +++++++
.../reindex/flyout/warnings_step.tsx | 104 ++---
.../application/mount_management_section.ts | 3 +-
.../es_migration_apis.test.ts.snap | 6 +
.../server/lib/es_migration_apis.ts | 10 +
.../lib/reindexing/index_settings.test.ts | 238 ++++++----
.../server/lib/reindexing/index_settings.ts | 109 +++--
.../upgrade_assistant/server/plugin.ts | 2 +
.../reindex_indices/reindex_indices.test.ts | 23 +-
.../server/routes/update_index_settings.ts | 65 +++
.../helpers/http_requests.ts | 56 +++
.../tests_client_integration/helpers/index.ts | 5 +-
.../helpers/indices.helpers.ts | 62 +++
.../helpers/setup_environment.tsx | 22 +-
.../tests_client_integration/indices.test.ts | 93 ++++
.../tests_client_integration/overview.test.ts | 10 +-
.../upgrade_assistant/upgrade_assistant.ts | 115 ++++-
44 files changed, 1417 insertions(+), 504 deletions(-)
create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/button.tsx
create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/index.ts
create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/remove_settings_provider.tsx
delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_warnings_step.scss
create mode 100644 x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step_checkbox.tsx
create mode 100644 x-pack/plugins/upgrade_assistant/server/routes/update_index_settings.ts
create mode 100644 x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/http_requests.ts
create mode 100644 x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/indices.helpers.ts
create mode 100644 x-pack/plugins/upgrade_assistant/tests_client_integration/indices.test.ts
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index 0a781394afc41..ee5f50588ff04 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -133,7 +133,8 @@ export class DocLinksService {
remoteClustersProxy: `${ELASTICSEARCH_DOCS}modules-remote-clusters.html#proxy-mode`,
remoteClusersProxySettings: `${ELASTICSEARCH_DOCS}modules-remote-clusters.html#remote-cluster-proxy-settings`,
scriptParameters: `${ELASTICSEARCH_DOCS}modules-scripting-using.html#prefer-params`,
- transportSettings: `${ELASTICSEARCH_DOCS}modules-network.html`,
+ transportSettings: `${ELASTICSEARCH_DOCS}modules-transport.html`,
+ typesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`,
},
siem: {
guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index c0c810496bb9a..32b749d2d7fa7 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -22801,7 +22801,6 @@
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutTitle": "インデックスが閉じました",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.openAndCloseDocumentation": "ドキュメンテーション",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.customTypeNameWarningDetail": "マッピングタイプは8.xではサポートされていません。このインデックスマッピングはデフォルトのタイプ名、{defaultType}を使用しておらず、再インデックス時に更新されます。アプリケーションコードまたはスクリプトが異なるタイプに依存していないことを確認してください。",
- "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.customTypeNameWarningTitle": "マッピングタイプが{defaultType}に変更されます",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail": "インデックスをバックアップして、互換性を破るそれぞれの変更に同意することで再インデックスしてください。",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle": "このインデックスには元に戻すことのできない破壊的な変更が含まれています",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.documentationLinkLabel": "ドキュメント",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 9a8c014dbc386..db3ca3d56ec5a 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -23158,7 +23158,6 @@
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.indexClosedCallout.calloutTitle": "索引已关闭",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.openAndCloseDocumentation": "文档",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.customTypeNameWarningDetail": "映射类型在 8.x 中不再受支持。此索引映射不使用默认类型名称 {defaultType},并将在重新索引时更新。确保没有应用程序代码或脚本依赖其他类型。",
- "xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.customTypeNameWarningTitle": "映射类型将更改为 {defaultType}",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail": "备份您的索引,然后通过接受每个重大更改来继续重新索引。",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle": "此索引需要无法撤消的破坏性更改",
"xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.documentationLinkLabel": "文档",
diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts
index 87b014c5bbc5b..cc8eaf9d66e04 100644
--- a/x-pack/plugins/upgrade_assistant/common/constants.ts
+++ b/x-pack/plugins/upgrade_assistant/common/constants.ts
@@ -20,3 +20,15 @@ export const mockKibanaSemverVersion = new SemVer(mockKibanaVersion);
* and will be presented with a message indicating as such.
*/
export const UA_READONLY_MODE = true;
+
+/*
+ * Map of 7.0 --> 8.0 index setting deprecation log messages and associated settings
+ * We currently only support one setting deprecation (translog retention), but the code is written
+ * in a way to be able to support any number of deprecated index settings defined here
+ */
+export const indexSettingDeprecations = {
+ translog: {
+ deprecationMessage: 'translog retention settings are ignored', // expected message from ES deprecation info API
+ settings: ['translog.retention.size', 'translog.retention.age'],
+ },
+};
diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts
index 6d83bdc5f36e9..b8a5a7c1ab8cc 100644
--- a/x-pack/plugins/upgrade_assistant/common/types.ts
+++ b/x-pack/plugins/upgrade_assistant/common/types.ts
@@ -92,11 +92,21 @@ export interface ReindexOperation extends SavedObjectAttributes {
export type ReindexSavedObject = SavedObject;
-export enum ReindexWarning {
- // 7.0 -> 8.0 warnings
- customTypeName,
-
- // 8.0 -> 9.0 warnings
+// 7.0 -> 8.0 warnings
+export type ReindexWarningTypes = 'customTypeName' | 'indexSetting';
+export interface ReindexWarning {
+ warningType: ReindexWarningTypes;
+ /**
+ * Optional metadata for deprecations
+ *
+ * @remark
+ * For example, for the "customTypeName" deprecation,
+ * we want to surface the typeName to the user.
+ * For "indexSetting" we want to surface the deprecated settings.
+ */
+ meta?: {
+ [key: string]: string | string[];
+ };
}
export enum IndexGroup {
@@ -181,6 +191,7 @@ export interface EnrichedDeprecationInfo extends DeprecationInfo {
index?: string;
node?: string;
reindex?: boolean;
+ deprecatedIndexSettings?: string[];
/**
* Indicate what blockers have been detected for calling reindex
* against this index.
diff --git a/x-pack/plugins/upgrade_assistant/public/application/app.tsx b/x-pack/plugins/upgrade_assistant/public/application/app.tsx
index d54a6957f4b6e..1276198a528df 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/app.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/app.tsx
@@ -18,9 +18,7 @@ export const RootComponent = ({ i18n, ...contextValue }: AppDependencies) => {
return (
-
+
);
diff --git a/x-pack/plugins/upgrade_assistant/public/application/app_context.tsx b/x-pack/plugins/upgrade_assistant/public/application/app_context.tsx
index 2a16be7ec5204..3dccd7b6959f2 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/app_context.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/app_context.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { DocLinksStart, HttpSetup } from 'src/core/public';
+import { DocLinksStart, HttpSetup, NotificationsStart } from 'src/core/public';
import React, { createContext, useContext } from 'react';
export interface KibanaVersionContext {
@@ -19,6 +19,7 @@ export interface ContextValue {
isCloudEnabled: boolean;
docLinks: DocLinksStart;
kibanaVersionInfo: KibanaVersionContext;
+ notifications: NotificationsStart;
isReadOnlyMode: boolean;
}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs.tsx
index fa6badb34635b..48dfc40af7392 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs.tsx
@@ -160,7 +160,8 @@ export class UpgradeAssistantTabs extends React.Component {
const resp = await this.props.http.get('/api/upgrade_assistant/status');
this.setState({
loadingState: LoadingState.Success,
- checkupData: resp,
+ // resp.data is specifically to handle the CITs which uses axios to mock HTTP requests
+ checkupData: resp.data ? resp.data : resp,
});
} catch (e) {
if (get(e, 'response.status') === 426) {
@@ -243,8 +244,7 @@ export class UpgradeAssistantTabs extends React.Component {
this.setState({ telemetryState: TelemetryState.Running });
- await this.props.http.fetch('/api/upgrade_assistant/stats/ui_open', {
- method: 'PUT',
+ await this.props.http.put('/api/upgrade_assistant/stats/ui_open', {
body: JSON.stringify(set({}, tabName, true)),
});
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap
index bac67bf722ea7..c4714796763ed 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap
@@ -60,225 +60,229 @@ exports[`CheckupTab render with deprecations 1`] = `
-
-
-
+
+
+
+
+
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.tsx
index 87c3d69ad03c6..f06abb33fc300 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/checkup_tab.tsx
@@ -162,7 +162,7 @@ export const CheckupTab: FunctionComponent = ({
{loadingState === LoadingState.Error ? (
) : deprecations && deprecations.length > 0 ? (
- <>
+
= ({
/>
{renderCheckupData()}
- >
+
) : (
;
reindexIndexName?: string;
+ deprecatedIndexSettings?: string[];
docUrl?: string;
headline?: string;
healthColor?: string;
@@ -38,6 +40,7 @@ export const DeprecationCell: FunctionComponent = ({
headline,
healthColor,
reindexIndexName,
+ deprecatedIndexSettings,
docUrl,
items = [],
children,
@@ -94,6 +97,12 @@ export const DeprecationCell: FunctionComponent = ({
)}
+
+ {deprecatedIndexSettings?.length && (
+
+
+
+ )}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx
index 91d34a3907d45..0885286961b11 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx
@@ -201,6 +201,7 @@ describe('GroupedDeprecations', () => {
describe('DeprecationAccordion', () => {
const defaultProps = {
id: 'x',
+ dataTestSubj: 'data-test-subj',
title: 'Issue 1',
currentGroupBy: GroupByOption.message,
forceExpand: false,
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx
index f27a2cf6cb2b6..3c18d6fe8a609 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx
@@ -65,7 +65,8 @@ export const DeprecationAccordion: FunctionComponent<{
title: string;
currentGroupBy: GroupByOption;
forceExpand: boolean;
-}> = ({ id, deprecations, title, currentGroupBy, forceExpand }) => {
+ dataTestSubj: string;
+}> = ({ id, deprecations, title, currentGroupBy, forceExpand, dataTestSubj }) => {
const hasIndices = Boolean(
currentGroupBy === GroupByOption.message && deprecations.filter((d) => d.index).length
);
@@ -74,6 +75,7 @@ export const DeprecationAccordion: FunctionComponent<{
return (
{title}}
@@ -223,6 +225,7 @@ export class GroupedDeprecations extends React.Component<
= ({ settings, index }) => {
+ return (
+
+ {(removeIndexSettingsPrompt, successfulRequests) => {
+ const isSuccessfulRequest = successfulRequests[index] === true;
+ return (
+ removeIndexSettingsPrompt(index, settings)}
+ isDisabled={isSuccessfulRequest}
+ iconType={isSuccessfulRequest ? 'check' : undefined}
+ >
+ {isSuccessfulRequest ? i18nTexts.doneButtonLabel : i18nTexts.fixButtonLabel}
+
+ );
+ }}
+
+ );
+};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/index.ts
new file mode 100644
index 0000000000000..e8a83790ee2a6
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/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 { FixIndexSettingsButton } from './button';
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/remove_settings_provider.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/remove_settings_provider.tsx
new file mode 100644
index 0000000000000..5078cf51e9d81
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_settings/remove_settings_provider.tsx
@@ -0,0 +1,137 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useState, useRef } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiCode, EuiConfirmModal } from '@elastic/eui';
+import { useAppContext } from '../../../../../app_context';
+
+interface Props {
+ children: (
+ removeSettingsPrompt: (index: string, settings: string[]) => void,
+ successfulRequests: { [key: string]: boolean }
+ ) => React.ReactNode;
+}
+
+const i18nTexts = {
+ removeButtonLabel: i18n.translate(
+ 'xpack.upgradeAssistant.checkupTab.confirmationModal.removeButtonLabel',
+ {
+ defaultMessage: 'Remove',
+ }
+ ),
+ cancelButtonLabel: i18n.translate(
+ 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.cancelButtonLabel',
+ {
+ defaultMessage: 'Cancel',
+ }
+ ),
+ modalDescription: i18n.translate(
+ 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.description',
+ {
+ defaultMessage: 'The following deprecated index settings were detected and will be removed:',
+ }
+ ),
+ successNotificationText: i18n.translate(
+ 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.successNotificationText',
+ {
+ defaultMessage: 'Index settings removed',
+ }
+ ),
+ errorNotificationText: i18n.translate(
+ 'xpack.upgradeAssistant.checkupTab.indexSettings.confirmationModal.errorNotificationText',
+ {
+ defaultMessage: 'Error removing index settings',
+ }
+ ),
+};
+
+export const RemoveIndexSettingsProvider = ({ children }: Props) => {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [successfulRequests, setSuccessfulRequests] = useState<{ [key: string]: boolean }>({});
+ const [isLoading, setIsLoading] = useState(false);
+
+ const deprecatedSettings = useRef([]);
+ const indexName = useRef(undefined);
+
+ const { http, notifications } = useAppContext();
+
+ const removeIndexSettings = async () => {
+ setIsLoading(true);
+ try {
+ await http.post(`/api/upgrade_assistant/${indexName.current}/index_settings`, {
+ body: JSON.stringify({
+ settings: deprecatedSettings.current,
+ }),
+ });
+
+ setIsLoading(false);
+ setSuccessfulRequests({
+ [indexName.current!]: true,
+ });
+ closeModal();
+ notifications.toasts.addSuccess(i18nTexts.successNotificationText);
+ } catch (e) {
+ setIsLoading(false);
+ closeModal();
+ notifications.toasts.addError(e, {
+ title: i18nTexts.errorNotificationText,
+ });
+ }
+ };
+
+ const closeModal = () => {
+ setIsModalOpen(false);
+ };
+
+ const removeSettingsPrompt = (index: string, settings: string[]) => {
+ setIsModalOpen(true);
+ setSuccessfulRequests({
+ [index]: false,
+ });
+ indexName.current = index;
+ deprecatedSettings.current = settings;
+ };
+
+ return (
+ <>
+ {children(removeSettingsPrompt, successfulRequests)}
+
+ {isModalOpen && (
+
+ <>
+ {i18nTexts.modalDescription}
+
+ {deprecatedSettings.current.map((setting, index) => (
+
+ {setting}
+
+ ))}
+
+ >
+
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.test.tsx
index e9defa76f5d0d..188e70b64ce6a 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.test.tsx
@@ -77,6 +77,7 @@ describe('IndexDeprecationTable', () => {
}
}
responsive={true}
+ rowProps={[Function]}
sorting={
Object {
"sort": Object {
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.tsx
index 292887853e4b3..da2f5f04effc4 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/index_table.tsx
@@ -13,12 +13,14 @@ import { i18n } from '@kbn/i18n';
import { ReindexButton } from './reindex';
import { AppContext } from '../../../../app_context';
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
+import { FixIndexSettingsButton } from './index_settings';
const PAGE_SIZES = [10, 25, 50, 100, 250, 500, 1000];
export interface IndexDeprecationDetails {
index: string;
reindex: boolean;
+ deprecatedIndexSettings?: string[];
blockerForReindexing?: EnrichedDeprecationInfo['blockerForReindexing'];
details?: string;
}
@@ -97,6 +99,11 @@ export class IndexDeprecationTable extends React.Component<
pagination={pagination}
onChange={this.onTableChange}
hasActions={false}
+ rowProps={(indexDetails) => {
+ return {
+ 'data-test-subj': `indexTableRow-${indexDetails.index}`,
+ };
+ }}
/>
);
}
@@ -142,12 +149,15 @@ export class IndexDeprecationTable extends React.Component<
}
private generateActionsColumn() {
- // NOTE: this naive implementation assumes all indices in the table are
- // should show the reindex button. This should work for known use cases.
+ // NOTE: this naive implementation assumes all indices in the table
+ // should show the reindex button or fix indices button. This should work for known use cases.
const { indices } = this.props;
- const hasActionsColumn = Boolean(indices.find((i) => i.reindex === true));
+ const showReindexButton = Boolean(indices.find((i) => i.reindex === true));
+ const showFixSettingsButton = Boolean(
+ indices.find((i) => i.deprecatedIndexSettings && i.deprecatedIndexSettings.length > 0)
+ );
- if (hasActionsColumn === false) {
+ if (showReindexButton === false && showFixSettingsButton === false) {
return null;
}
@@ -155,19 +165,28 @@ export class IndexDeprecationTable extends React.Component<
actions: [
{
render(indexDep: IndexDeprecationDetails) {
+ if (showReindexButton) {
+ return (
+
+ {({ http, docLinks }) => {
+ return (
+
+ );
+ }}
+
+ );
+ }
+
return (
-
- {({ http, docLinks }) => {
- return (
-
- );
- }}
-
+
);
},
},
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.test.tsx
index 0bd930484af04..1d646442ebee0 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.test.tsx
@@ -73,12 +73,14 @@ describe('DeprecationList', () => {
Array [
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": undefined,
"details": undefined,
"index": "0",
"reindex": false,
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": undefined,
"details": undefined,
"index": "1",
"reindex": false,
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx
index a3e024ee54669..eb9012b3bddd1 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx
@@ -21,9 +21,9 @@ const sortByLevelDesc = (a: DeprecationInfo, b: DeprecationInfo) => {
/**
* Used to show a single deprecation message with any detailed information.
*/
-const MessageDeprecation: FunctionComponent<{ deprecation: EnrichedDeprecationInfo }> = ({
- deprecation,
-}) => {
+const MessageDeprecation: FunctionComponent<{
+ deprecation: EnrichedDeprecationInfo;
+}> = ({ deprecation }) => {
const items = [];
if (deprecation.details) {
@@ -36,6 +36,7 @@ const MessageDeprecation: FunctionComponent<{ deprecation: EnrichedDeprecationIn
headline={deprecation.message}
healthColor={COLOR_MAP[deprecation.level]}
reindexIndexName={deprecation.reindex ? deprecation.index! : undefined}
+ deprecatedIndexSettings={deprecation.deprecatedIndexSettings}
docUrl={deprecation.url}
items={items}
/>
@@ -59,12 +60,13 @@ const SimpleMessageDeprecation: FunctionComponent<{ deprecation: EnrichedDepreca
reindexBlocker={deprecation.blockerForReindexing}
items={items}
docUrl={deprecation.url}
+ deprecatedIndexSettings={deprecation.deprecatedIndexSettings}
/>
);
};
interface IndexDeprecationProps {
- deprecation: DeprecationInfo;
+ deprecation: EnrichedDeprecationInfo;
indices: IndexDeprecationDetails[];
}
@@ -96,6 +98,7 @@ export const DeprecationList: FunctionComponent<{
index: dep.index!,
details: dep.details,
reindex: dep.reindex === true,
+ deprecatedIndexSettings: dep.deprecatedIndexSettings,
blockerForReindexing: dep.blockerForReindexing,
}));
return ;
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/button.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/button.tsx
index 1a99eca816007..2692dd083c3b8 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/button.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/button.tsx
@@ -240,8 +240,7 @@ export class ReindexButton extends React.Component
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_index.scss
index 9e899eee88796..1c9fd599b13a8 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_index.scss
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_index.scss
@@ -1,2 +1 @@
@import 'step_progress';
-@import 'warnings_step';
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_warnings_step.scss b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_warnings_step.scss
deleted file mode 100644
index 4279c0110ea75..0000000000000
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/_warnings_step.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-.upgWarningsStep__warningDescription {
- margin-left: $euiSizeL;
- margin-top: $euiSizeXS;
-}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/checklist_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/checklist_step.test.tsx
index feca7fe392bd8..5bea0d855e45e 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/checklist_step.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/checklist_step.test.tsx
@@ -9,7 +9,7 @@ import { shallow } from 'enzyme';
import { cloneDeep } from 'lodash';
import React from 'react';
-import { ReindexStatus, ReindexWarning } from '../../../../../../../../common/types';
+import { ReindexStatus } from '../../../../../../../../common/types';
import { LoadingState } from '../../../../../types';
import { ReindexState } from '../polling_service';
import { ChecklistFlyoutStep } from './checklist_step';
@@ -29,7 +29,7 @@ describe('ChecklistFlyout', () => {
status: undefined,
reindexTaskPercComplete: null,
errorMessage: null,
- reindexWarnings: [ReindexWarning.customTypeName],
+ reindexWarnings: [],
hasRequiredPrivileges: true,
} as ReindexState,
};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx
index d365cd82ba86c..ca9c53354bf75 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step.test.tsx
@@ -8,19 +8,21 @@
import { I18nProvider } from '@kbn/i18n/react';
import { mount, shallow } from 'enzyme';
import React from 'react';
-import { mockKibanaSemverVersion } from '../../../../../../../../common/constants';
import { ReindexWarning } from '../../../../../../../../common/types';
+import { mockKibanaSemverVersion } from '../../../../../../../../common/constants';
+
import { idForWarning, WarningsFlyoutStep } from './warnings_step';
jest.mock('../../../../../../app_context', () => {
+ const { docLinksServiceMock } = jest.requireActual(
+ '../../../../../../../../../../../src/core/public/doc_links/doc_links_service.mock'
+ );
+
return {
useAppContext: () => {
return {
- docLinks: {
- DOC_LINK_VERSION: 'current',
- ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
- },
+ docLinks: docLinksServiceMock.createStartContract(),
kibanaVersionInfo: {
currentMajor: mockKibanaSemverVersion.major,
prevMajor: mockKibanaSemverVersion.major - 1,
@@ -34,7 +36,7 @@ jest.mock('../../../../../../app_context', () => {
describe('WarningsFlyoutStep', () => {
const defaultProps = {
advanceNextStep: jest.fn(),
- warnings: [ReindexWarning.customTypeName],
+ warnings: [] as ReindexWarning[],
closeFlyout: jest.fn(),
renderGlobalCallouts: jest.fn(),
};
@@ -45,19 +47,40 @@ describe('WarningsFlyoutStep', () => {
if (mockKibanaSemverVersion.major === 7) {
it('does not allow proceeding until all are checked', () => {
+ const defaultPropsWithWarnings = {
+ ...defaultProps,
+ warnings: [
+ {
+ warningType: 'customTypeName',
+ meta: {
+ typeName: 'my_mapping_type',
+ },
+ },
+ {
+ warningType: 'indexSetting',
+ meta: {
+ deprecatedSettings: ['index.force_memory_term_dictionary'],
+ },
+ },
+ ] as ReindexWarning[],
+ };
const wrapper = mount(
-
+
);
const button = wrapper.find('EuiButton');
button.simulate('click');
- expect(defaultProps.advanceNextStep).not.toHaveBeenCalled();
+ expect(defaultPropsWithWarnings.advanceNextStep).not.toHaveBeenCalled();
- wrapper.find(`input#${idForWarning(ReindexWarning.customTypeName)}`).simulate('change');
+ // first warning (customTypeName)
+ wrapper.find(`input#${idForWarning(0)}`).simulate('change');
+ // second warning (indexSetting)
+ wrapper.find(`input#${idForWarning(1)}`).simulate('change');
button.simulate('click');
- expect(defaultProps.advanceNextStep).toHaveBeenCalled();
+
+ expect(defaultPropsWithWarnings.advanceNextStep).toHaveBeenCalled();
});
}
});
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step_checkbox.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step_checkbox.tsx
new file mode 100644
index 0000000000000..19952938a1dd9
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warning_step_checkbox.tsx
@@ -0,0 +1,161 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license 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 {
+ EuiCheckbox,
+ EuiCode,
+ EuiLink,
+ EuiSpacer,
+ EuiText,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIconTip,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { DocLinksStart } from 'kibana/public';
+import { ReindexWarning, ReindexWarningTypes } from '../../../../../../../../common/types';
+
+export const hasReindexWarning = (
+ warnings: ReindexWarning[],
+ warningType: ReindexWarningTypes
+): boolean => {
+ return Boolean(warnings.find((warning) => warning.warningType === warningType));
+};
+
+const WarningCheckbox: React.FunctionComponent<{
+ isChecked: boolean;
+ warningId: string;
+ label: React.ReactNode;
+ description: React.ReactNode;
+ documentationUrl: string;
+ onChange: (event: React.ChangeEvent) => void;
+}> = ({ isChecked, warningId, label, onChange, description, documentationUrl }) => (
+ <>
+
+
+
+ {label}}
+ checked={isChecked}
+ onChange={onChange}
+ />
+
+
+
+
+ }
+ position="right"
+ type="help"
+ />
+
+
+
+
+
+
+ {description}
+
+
+
+ >
+);
+
+export interface WarningCheckboxProps {
+ isChecked: boolean;
+ onChange: (event: React.ChangeEvent) => void;
+ docLinks: DocLinksStart['links'];
+ id: string;
+ meta?: ReindexWarning['meta'];
+}
+
+export const CustomTypeNameWarningCheckbox: React.FunctionComponent = ({
+ isChecked,
+ onChange,
+ docLinks,
+ id,
+ meta,
+}) => {
+ return (
+ {meta!.typeName as string},
+ defaultType: _doc ,
+ }}
+ />
+ }
+ description={
+ _doc,
+ }}
+ />
+ }
+ documentationUrl={docLinks.elasticsearch.typesRemoval}
+ />
+ );
+};
+
+export const DeprecatedSettingWarningCheckbox: React.FunctionComponent = ({
+ isChecked,
+ onChange,
+ docLinks,
+ id,
+ meta,
+}) => {
+ return (
+
+ }
+ description={
+ <>
+
+
+
+
+
+ {(meta!.deprecatedSettings as string[]).map((setting, index) => {
+ return (
+
+ {setting}
+
+ );
+ })}
+
+ >
+ }
+ documentationUrl={docLinks.elasticsearch.indexModules}
+ />
+ );
+};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx
index f6620e4125c9a..d0904c588ed07 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx
@@ -10,59 +10,35 @@ import React, { useState } from 'react';
import {
EuiButton,
EuiButtonEmpty,
- EuiCode,
EuiCallOut,
- EuiCheckbox,
EuiFlexGroup,
EuiFlexItem,
EuiFlyoutBody,
EuiFlyoutFooter,
- EuiLink,
EuiSpacer,
- EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
+
+import { ReindexWarning, ReindexWarningTypes } from '../../../../../../../../common/types';
import { useAppContext } from '../../../../../../app_context';
-import { ReindexWarning } from '../../../../../../../../common/types';
+import {
+ CustomTypeNameWarningCheckbox,
+ DeprecatedSettingWarningCheckbox,
+ WarningCheckboxProps,
+} from './warning_step_checkbox';
interface CheckedIds {
[id: string]: boolean;
}
-export const idForWarning = (warning: ReindexWarning) => `reindexWarning-${warning}`;
-
-const WarningCheckbox: React.FunctionComponent<{
- checkedIds: CheckedIds;
- warning: ReindexWarning;
- label: React.ReactNode;
- description: React.ReactNode;
- documentationUrl: string;
- onChange: (event: React.ChangeEvent) => void;
-}> = ({ checkedIds, warning, label, onChange, description, documentationUrl }) => (
- <>
-
- {label}}
- checked={checkedIds[idForWarning(warning)]}
- onChange={onChange}
- />
-
- {description}
-
-
-
-
-
-
-
-
- >
-);
+const warningToComponentMap: {
+ [key in ReindexWarningTypes]: React.FunctionComponent;
+} = {
+ customTypeName: CustomTypeNameWarningCheckbox,
+ indexSetting: DeprecatedSettingWarningCheckbox,
+};
+export const idForWarning = (id: number) => `reindexWarning-${id}`;
interface WarningsConfirmationFlyoutProps {
renderGlobalCallouts: () => React.ReactNode;
closeFlyout: () => void;
@@ -80,9 +56,12 @@ export const WarningsFlyoutStep: React.FunctionComponent {
+ const { docLinks } = useAppContext();
+ const { links } = docLinks;
+
const [checkedIds, setCheckedIds] = useState(
- warnings.reduce((initialCheckedIds, warning) => {
- initialCheckedIds[idForWarning(warning)] = false;
+ warnings.reduce((initialCheckedIds, warning, index) => {
+ initialCheckedIds[idForWarning(index)] = false;
return initialCheckedIds;
}, {} as { [id: string]: boolean })
);
@@ -101,14 +80,11 @@ export const WarningsFlyoutStep: React.FunctionComponent
{renderGlobalCallouts()}
+
- {kibanaVersionInfo.currentMajor === 7 && warnings.includes(ReindexWarning.customTypeName) && (
- _doc,
- }}
- />
- }
- description={
- _doc,
- }}
- />
- }
- documentationUrl={`${esDocBasePath}/${DOC_LINK_VERSION}/removal-of-types.html`}
- />
- )}
+ {warnings.map((warning, index) => {
+ const WarningCheckbox = warningToComponentMap[warning.warningType];
+ return (
+
+ );
+ })}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts b/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts
index 905f8201e0648..cb45247473465 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts
+++ b/x-pack/plugins/upgrade_assistant/public/application/mount_management_section.ts
@@ -17,7 +17,7 @@ export async function mountManagementSection(
params: ManagementAppMountParams,
kibanaVersionInfo: KibanaVersionContext
) {
- const [{ i18n, docLinks }] = await coreSetup.getStartServices();
+ const [{ i18n, docLinks, notifications }] = await coreSetup.getStartServices();
return renderApp({
element: params.element,
isCloudEnabled,
@@ -25,6 +25,7 @@ export async function mountManagementSection(
i18n,
docLinks,
kibanaVersionInfo,
+ notifications,
isReadOnlyMode: UA_READONLY_MODE,
});
}
diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap
index 244fc96acd194..aefac2b4c63f6 100644
--- a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap
+++ b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap
@@ -31,6 +31,7 @@ Object {
"indices": Array [
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]",
"index": ".monitoring-es-6-2018.11.07",
"level": "warning",
@@ -40,6 +41,7 @@ Object {
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: tweet, field: liked]]",
"index": "twitter",
"level": "warning",
@@ -49,6 +51,7 @@ Object {
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]",
"index": ".kibana",
"level": "warning",
@@ -58,6 +61,7 @@ Object {
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]",
"index": ".watcher-history-6-2018.11.07",
"level": "warning",
@@ -67,6 +71,7 @@ Object {
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: doc, field: snapshot]]",
"index": ".monitoring-kibana-6-2018.11.07",
"level": "warning",
@@ -76,6 +81,7 @@ Object {
},
Object {
"blockerForReindexing": undefined,
+ "deprecatedIndexSettings": Array [],
"details": "[[type: tweet, field: liked]]",
"index": "twitter2",
"level": "warning",
diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts
index 6e357fd755c82..3486603341674 100644
--- a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts
+++ b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts
@@ -6,6 +6,7 @@
*/
import { IScopedClusterClient } from 'src/core/server';
+import { indexSettingDeprecations } from '../../common/constants';
import {
DeprecationAPIResponse,
EnrichedDeprecationInfo,
@@ -57,6 +58,7 @@ const getCombinedIndexInfos = (deprecations: DeprecationAPIResponse) =>
...d,
index: indexName,
reindex: /Index created before/.test(d.message),
+ deprecatedIndexSettings: getIndexSettingDeprecations(d.message),
} as EnrichedDeprecationInfo)
)
);
@@ -74,3 +76,11 @@ const getClusterDeprecations = (deprecations: DeprecationAPIResponse, isCloudEna
return combined;
}
};
+
+const getIndexSettingDeprecations = (message: string) => {
+ const indexDeprecation = Object.values(indexSettingDeprecations).find(
+ ({ deprecationMessage }) => deprecationMessage === message
+ );
+
+ return indexDeprecation?.settings || [];
+};
diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts
index f778981b95054..5e994f76f927c 100644
--- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts
+++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts
@@ -6,7 +6,6 @@
*/
import { mockKibanaSemverVersion, mockKibanaVersion } from '../../../common/constants';
-import { ReindexWarning } from '../../../common/types';
import { versionService } from '../version';
import { getMockVersionInfo } from '../__fixtures__/version';
@@ -64,6 +63,11 @@ describe('transformFlatSettings', () => {
'index.verified_before_close': 'true',
'index.version.created': '123123',
'index.version.upgraded': '123123',
+
+ // Deprecated settings
+ 'index.force_memory_term_dictionary': '1024',
+ 'index.max_adjacency_matrix_filters': 'true',
+ 'index.soft_deletes.enabled': 'true',
},
mappings: {},
})
@@ -76,129 +80,173 @@ describe('transformFlatSettings', () => {
});
});
- it('does not allow index.mapper.dynamic to be set', () => {
- expect(() =>
+ it('removes index.translog.retention.size if soft deletes is enabled', () => {
+ expect(
transformFlatSettings({
settings: {
- 'index.mapper.dynamic': 'true',
- },
- mappings: {},
- })
- ).toThrowError(`'index.mapper.dynamic' is no longer supported.`);
- });
+ // Settings that should get preserved
+ 'index.number_of_replicas': '1',
+ 'index.number_of_shards': '5',
- it('does not allow index.merge.policy.reclaim_deletes_weight to be set', () => {
- expect(() =>
- transformFlatSettings({
- settings: {
- 'index.merge.policy.reclaim_deletes_weight': '2.0d',
+ // Deprecated settings
+ 'index.soft_deletes.enabled': 'true',
+ 'index.translog.retention.size': '5b',
},
mappings: {},
})
- ).toThrowError(`'index.merge.policy.reclaim_deletes_weight' is no longer supported.`);
+ ).toEqual({
+ settings: {
+ 'index.number_of_replicas': '1',
+ 'index.number_of_shards': '5',
+ },
+ mappings: {},
+ });
});
- it('does not allow index.force_memory_term_dictionary to be set', () => {
- expect(() =>
+ it('removes index.translog.retention.age if soft deletes is enabled', () => {
+ expect(
transformFlatSettings({
settings: {
- 'index.force_memory_term_dictionary': 'false',
- },
- mappings: {},
- })
- ).toThrowError(`'index.force_memory_term_dictionary' is no longer supported.`);
- });
+ // Settings that should get preserved
+ 'index.number_of_replicas': '1',
+ 'index.number_of_shards': '5',
- it('does not index.max_adjacency_matrix_filters to be set', () => {
- expect(() =>
- transformFlatSettings({
- settings: {
- 'index.max_adjacency_matrix_filters': '1024',
+ // Deprecated settings
+ 'index.soft_deletes.enabled': 'true',
+ 'index.translog.retention.age': '5d',
},
mappings: {},
})
- ).toThrowError(
- `'index.max_adjacency_matrix_filters' is no longer supported; use 'indices.query.bool.max_clause_count' as an alternative.`
- );
+ ).toEqual({
+ settings: {
+ 'index.number_of_replicas': '1',
+ 'index.number_of_shards': '5',
+ },
+ mappings: {},
+ });
});
-});
-describe('sourceNameForIndex', () => {
- beforeEach(() => {
- versionService.setup(mockKibanaVersion);
- });
+ describe('sourceNameForIndex', () => {
+ beforeEach(() => {
+ versionService.setup(mockKibanaVersion);
+ });
- it('parses internal indices', () => {
- expect(sourceNameForIndex('.myInternalIndex')).toEqual('.myInternalIndex');
- });
+ it('parses internal indices', () => {
+ expect(sourceNameForIndex('.myInternalIndex')).toEqual('.myInternalIndex');
+ });
- it('parses non-internal indices', () => {
- expect(sourceNameForIndex('myIndex')).toEqual('myIndex');
- });
+ it('parses non-internal indices', () => {
+ expect(sourceNameForIndex('myIndex')).toEqual('myIndex');
+ });
- it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in newIndexName`, () => {
- expect(sourceNameForIndex(`reindexed-v${prevMajor}-myIndex`)).toEqual('myIndex');
- expect(sourceNameForIndex(`.reindexed-v${prevMajor}-myInternalIndex`)).toEqual(
- '.myInternalIndex'
- );
+ it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in newIndexName`, () => {
+ expect(sourceNameForIndex(`reindexed-v${prevMajor}-myIndex`)).toEqual('myIndex');
+ expect(sourceNameForIndex(`.reindexed-v${prevMajor}-myInternalIndex`)).toEqual(
+ '.myInternalIndex'
+ );
+ });
});
-});
-describe('generateNewIndexName', () => {
- beforeEach(() => {
- versionService.setup(mockKibanaVersion);
- });
+ describe('generateNewIndexName', () => {
+ beforeEach(() => {
+ versionService.setup(mockKibanaVersion);
+ });
- it('parses internal indices', () => {
- expect(generateNewIndexName('.myInternalIndex')).toEqual(
- `.reindexed-v${currentMajor}-myInternalIndex`
- );
- });
+ it('parses internal indices', () => {
+ expect(generateNewIndexName('.myInternalIndex')).toEqual(
+ `.reindexed-v${currentMajor}-myInternalIndex`
+ );
+ });
- it('parses non-internal indices', () => {
- expect(generateNewIndexName('myIndex')).toEqual(`reindexed-v${currentMajor}-myIndex`);
- });
+ it('parses non-internal indices', () => {
+ expect(generateNewIndexName('myIndex')).toEqual(`reindexed-v${currentMajor}-myIndex`);
+ });
- it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in generateNewIndexName`, () => {
- expect(generateNewIndexName(`reindexed-v${prevMajor}-myIndex`)).toEqual(
- `reindexed-v${currentMajor}-myIndex`
- );
+ it(`replaces reindexed-v${prevMajor} with reindexed-v${currentMajor} in generateNewIndexName`, () => {
+ expect(generateNewIndexName(`reindexed-v${prevMajor}-myIndex`)).toEqual(
+ `reindexed-v${currentMajor}-myIndex`
+ );
- expect(generateNewIndexName(`.reindexed-v${prevMajor}-myInternalIndex`)).toEqual(
- `.reindexed-v${currentMajor}-myInternalIndex`
- );
+ expect(generateNewIndexName(`.reindexed-v${prevMajor}-myInternalIndex`)).toEqual(
+ `.reindexed-v${currentMajor}-myInternalIndex`
+ );
+ });
});
-});
-describe('getReindexWarnings', () => {
- it('does not blow up for empty mappings', () => {
- expect(
- getReindexWarnings({
- settings: {},
- mappings: {},
- })
- ).toEqual([]);
- });
+ describe('getReindexWarnings', () => {
+ it('does not blow up for empty mappings', () => {
+ expect(
+ getReindexWarnings({
+ settings: {},
+ mappings: {},
+ })
+ ).toEqual([]);
+ });
- if (mockKibanaSemverVersion.major === 7) {
- describe('customTypeName warning', () => {
- it('returns customTypeName for non-_doc mapping types', () => {
- expect(
- getReindexWarnings({
- settings: {},
- mappings: { doc: {} },
- })
- ).toEqual([ReindexWarning.customTypeName]);
+ if (mockKibanaSemverVersion.major === 7) {
+ describe('[7.x] customTypeName warning', () => {
+ it('returns customTypeName warning for non-_doc mapping types', () => {
+ expect(
+ getReindexWarnings({
+ settings: {},
+ mappings: { doc: {} },
+ })
+ ).toEqual([
+ {
+ warningType: 'customTypeName',
+ meta: {
+ typeName: 'doc',
+ },
+ },
+ ]);
+ });
+ it('does not return customTypeName warning for _doc mapping types', () => {
+ expect(
+ getReindexWarnings({
+ settings: {},
+ mappings: { _doc: {} },
+ })
+ ).toEqual([]);
+ });
});
- it('does not return customTypeName for _doc mapping types', () => {
- expect(
- getReindexWarnings({
- settings: {},
- mappings: { _doc: {} },
- })
- ).toEqual([]);
+ describe('[7.x] deprecatedSetting warning', () => {
+ it('returns deprecatedSetting warning for deprecated index settings', () => {
+ expect(
+ getReindexWarnings({
+ settings: {
+ // Deprecated settings
+ 'index.force_memory_term_dictionary': '1024',
+ 'index.max_adjacency_matrix_filters': 'true',
+ 'index.soft_deletes.enabled': 'true',
+ },
+ mappings: {},
+ })
+ ).toEqual([
+ {
+ warningType: 'indexSetting',
+ meta: {
+ deprecatedSettings: [
+ 'index.force_memory_term_dictionary',
+ 'index.max_adjacency_matrix_filters',
+ 'index.soft_deletes.enabled',
+ ],
+ },
+ },
+ ]);
+ });
+
+ it('does not return a deprecatedSetting warning for there are no deprecated index settings', () => {
+ expect(
+ getReindexWarnings({
+ settings: {
+ 'index.number_of_replicas': '1',
+ },
+ mappings: {},
+ })
+ ).toEqual([]);
+ });
});
- });
- }
+ }
+ });
});
diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts
index 70e1992d5b3e9..5ee067f3640f3 100644
--- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts
+++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts
@@ -15,6 +15,17 @@ export interface ParsedIndexName {
newIndexName: string;
cleanBaseName: string;
}
+/**
+ * An array of deprecated index settings specific to 7.0 --> 8.0 upgrade
+ * This excludes the deprecated translog retention settings
+ * as these are only marked as deprecated if soft deletes is enabled
+ * See logic in getDeprecatedSettingWarning() for more details
+ */
+const deprecatedSettings = [
+ 'index.force_memory_term_dictionary',
+ 'index.max_adjacency_matrix_filters',
+ 'index.soft_deletes.enabled',
+];
/**
* Validates, and updates deprecated settings and mappings to be applied to the
@@ -64,6 +75,55 @@ export const generateNewIndexName = (indexName: string): string => {
: `${currentVersion}-${sourceName}`;
};
+export const getCustomTypeWarning = (
+ flatSettings: FlatSettingsWithTypeName | FlatSettings
+): ReindexWarning | undefined => {
+ const DEFAULT_TYPE_NAME = '_doc';
+ // In 7+, it's not possible to have more than one type,
+ // so always grab the first (and only) key.
+ const typeName = Object.getOwnPropertyNames(flatSettings.mappings)[0];
+ const typeNameWarning = Boolean(typeName && typeName !== DEFAULT_TYPE_NAME);
+
+ if (typeNameWarning) {
+ return {
+ warningType: 'customTypeName',
+ meta: {
+ typeName,
+ },
+ };
+ }
+};
+
+export const getDeprecatedSettingWarning = (
+ flatSettings: FlatSettingsWithTypeName | FlatSettings
+): ReindexWarning | undefined => {
+ const { settings } = flatSettings;
+
+ const deprecatedSettingsInUse = Object.keys(settings).filter((setting) => {
+ return deprecatedSettings.indexOf(setting) > -1;
+ });
+
+ // Translog settings are only marked as deprecated if soft deletes is enabled
+ if (settings['index.soft_deletes.enabled'] === 'true') {
+ if (settings['index.translog.retention.size']) {
+ deprecatedSettingsInUse.push('index.translog.retention.size');
+ }
+
+ if (settings['index.translog.retention.age']) {
+ deprecatedSettingsInUse.push('index.translog.retention.age');
+ }
+ }
+
+ if (deprecatedSettingsInUse.length) {
+ return {
+ warningType: 'indexSetting',
+ meta: {
+ deprecatedSettings: deprecatedSettingsInUse,
+ },
+ };
+ }
+};
+
/**
* Returns an array of warnings that should be displayed to user before reindexing begins.
* @param flatSettings
@@ -71,22 +131,22 @@ export const generateNewIndexName = (indexName: string): string => {
export const getReindexWarnings = (
flatSettings: FlatSettingsWithTypeName | FlatSettings
): ReindexWarning[] => {
- const warnings = [
- // No warnings yet for 8.0 -> 9.0
- ] as Array<[ReindexWarning, boolean]>;
+ const warnings = [] as ReindexWarning[];
if (versionService.getMajorVersion() === 7) {
- const DEFAULT_TYPE_NAME = '_doc';
- // In 7+ it's not possible to have more than one type anyways, so always grab the first
- // (and only) key.
- const typeName = Object.getOwnPropertyNames(flatSettings.mappings)[0];
+ const customTypeWarning = getCustomTypeWarning(flatSettings);
+ const deprecatedSettingWarning = getDeprecatedSettingWarning(flatSettings);
- const typeNameWarning = Boolean(typeName && typeName !== DEFAULT_TYPE_NAME);
+ if (customTypeWarning) {
+ warnings.push(customTypeWarning);
+ }
- warnings.push([ReindexWarning.customTypeName, typeNameWarning]);
+ if (deprecatedSettingWarning) {
+ warnings.push(deprecatedSettingWarning);
+ }
}
- return warnings.filter(([_, applies]) => applies).map(([warning, _]) => warning);
+ return warnings;
};
const removeUnsettableSettings = (settings: FlatSettings['settings']) =>
@@ -119,30 +179,25 @@ const removeUnsettableSettings = (settings: FlatSettings['settings']) =>
'index.version.upgraded',
]);
-const validateSettings = (settings: FlatSettings['settings']) => {
- if (settings['index.mapper.dynamic']) {
- throw new Error(`'index.mapper.dynamic' is no longer supported.`);
- }
-
- if (settings['index.merge.policy.reclaim_deletes_weight']) {
- throw new Error(`'index.merge.policy.reclaim_deletes_weight' is no longer supported.`);
- }
+const removeDeprecatedSettings = (settings: FlatSettings['settings']) => {
+ const updatedSettings = { ...settings };
- if (settings['index.force_memory_term_dictionary']) {
- throw new Error(`'index.force_memory_term_dictionary' is no longer supported.`);
- }
+ // Translog settings are only marked as deprecated if soft deletes is enabled
+ if (updatedSettings['index.soft_deletes.enabled'] === 'true') {
+ if (updatedSettings['index.translog.retention.size']) {
+ delete updatedSettings['index.translog.retention.size'];
+ }
- if (settings['index.max_adjacency_matrix_filters']) {
- throw new Error(
- `'index.max_adjacency_matrix_filters' is no longer supported; use 'indices.query.bool.max_clause_count' as an alternative.`
- );
+ if (settings['index.translog.retention.age']) {
+ delete updatedSettings['index.translog.retention.age'];
+ }
}
- return settings;
+ return omit(updatedSettings, deprecatedSettings);
};
// Use `flow` to pipe the settings through each function.
-const transformSettings = flow(removeUnsettableSettings, validateSettings);
+const transformSettings = flow(removeUnsettableSettings, removeDeprecatedSettings);
const updateFixableMappings = (mappings: FlatSettings['mappings']) => {
return mappings;
diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts
index c6da641f4092f..ae5975c2bc8a7 100644
--- a/x-pack/plugins/upgrade_assistant/server/plugin.ts
+++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts
@@ -29,6 +29,7 @@ import { registerClusterCheckupRoutes } from './routes/cluster_checkup';
import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging';
import { registerReindexIndicesRoutes, createReindexWorker } from './routes/reindex_indices';
import { registerTelemetryRoutes } from './routes/telemetry';
+import { registerUpdateSettingsRoute } from './routes/update_index_settings';
import { telemetrySavedObjectType, reindexOperationSavedObjectType } from './saved_object_types';
import { RouteDependencies } from './types';
@@ -111,6 +112,7 @@ export class UpgradeAssistantServerPlugin implements Plugin {
registerReindexIndicesRoutes(dependencies, this.getWorker.bind(this));
// Bootstrap the needed routes and the collector for the telemetry
registerTelemetryRoutes(dependencies);
+ registerUpdateSettingsRoute(dependencies);
if (usageCollection) {
getStartServices().then(([{ savedObjects: savedObjectsService, elasticsearch }]) => {
diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts
index 21dded346bbd3..08d9995ee6219 100644
--- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts
+++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.test.ts
@@ -31,12 +31,7 @@ jest.mock('../../lib/reindexing', () => {
};
});
-import {
- IndexGroup,
- ReindexSavedObject,
- ReindexStatus,
- ReindexWarning,
-} from '../../../common/types';
+import { IndexGroup, ReindexSavedObject, ReindexStatus } from '../../../common/types';
import { credentialStoreFactory } from '../../lib/reindexing/credential_store';
import { registerReindexIndicesRoutes } from './reindex_indices';
@@ -90,7 +85,12 @@ describe('reindex API', () => {
attributes: { indexName: 'wowIndex', status: ReindexStatus.inProgress },
});
mockReindexService.detectReindexWarnings.mockResolvedValueOnce([
- ReindexWarning.customTypeName,
+ {
+ warningType: 'customTypeName',
+ meta: {
+ typeName: 'my_mapping_type',
+ },
+ },
]);
const resp = await routeDependencies.router.getHandler({
@@ -110,7 +110,14 @@ describe('reindex API', () => {
expect(resp.status).toEqual(200);
const data = resp.payload;
expect(data.reindexOp).toEqual({ indexName: 'wowIndex', status: ReindexStatus.inProgress });
- expect(data.warnings).toEqual([0]);
+ expect(data.warnings).toEqual([
+ {
+ warningType: 'customTypeName',
+ meta: {
+ typeName: 'my_mapping_type',
+ },
+ },
+ ]);
});
it("returns null for both if reindex operation doesn't exist and index doesn't exist", async () => {
diff --git a/x-pack/plugins/upgrade_assistant/server/routes/update_index_settings.ts b/x-pack/plugins/upgrade_assistant/server/routes/update_index_settings.ts
new file mode 100644
index 0000000000000..6dfafbea00816
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/server/routes/update_index_settings.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
+import { RouteDependencies } from '../types';
+
+export function registerUpdateSettingsRoute({ router }: RouteDependencies) {
+ router.post(
+ {
+ path: '/api/upgrade_assistant/{indexName}/index_settings',
+ validate: {
+ params: schema.object({
+ indexName: schema.string(),
+ }),
+ body: schema.object({
+ settings: schema.arrayOf(schema.string()),
+ }),
+ },
+ },
+ versionCheckHandlerWrapper(
+ async (
+ {
+ core: {
+ elasticsearch: { client },
+ },
+ },
+ request,
+ response
+ ) => {
+ try {
+ const { indexName } = request.params;
+ const { settings } = request.body;
+
+ const settingsToDelete = settings.reduce((settingsBody, currentSetting) => {
+ settingsBody[currentSetting] = null;
+ return settingsBody;
+ }, {} as { [key: string]: null });
+
+ const { body: settingsResponse } = await client.asCurrentUser.indices.putSettings({
+ index: indexName,
+ body: {
+ index: settingsToDelete,
+ },
+ });
+
+ return response.ok({
+ body: settingsResponse,
+ });
+ } catch (e) {
+ const status = e.status || e.statusCode;
+ if (status === 403) {
+ return response.forbidden({ body: e });
+ }
+
+ throw e;
+ }
+ }
+ )
+ );
+}
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/http_requests.ts
new file mode 100644
index 0000000000000..73aa997fa568b
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/http_requests.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import sinon, { SinonFakeServer } from 'sinon';
+import { UpgradeAssistantStatus } from '../../common/types';
+
+// Register helpers to mock HTTP Requests
+const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
+ const setLoadStatusResponse = (
+ response?: UpgradeAssistantStatus,
+ error?: { body?: Error; status: number }
+ ) => {
+ const status = error ? error.status || 400 : 200;
+ const body = error ? error.body : response;
+
+ server.respondWith('GET', '/api/upgrade_assistant/status', [
+ status,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify(body),
+ ]);
+ };
+
+ const setUpdateIndexSettingsResponse = (response?: object) => {
+ server.respondWith('POST', `/api/upgrade_assistant/:indexName/index_settings`, [
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify(response),
+ ]);
+ };
+
+ return {
+ setLoadStatusResponse,
+ setUpdateIndexSettingsResponse,
+ };
+};
+
+export const init = () => {
+ const server = sinon.fakeServer.create();
+ server.respondImmediately = true;
+
+ // Define default response for unhandled requests.
+ // We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry,
+ // and we can mock them all with a 200 instead of mocking each one individually.
+ server.respondWith([200, {}, 'DefaultMockedResponse']);
+
+ const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server);
+
+ return {
+ server,
+ httpRequestsMockHelpers,
+ };
+};
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/index.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/index.ts
index 358c854a0af9f..74aa173866b7a 100644
--- a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/index.ts
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/index.ts
@@ -5,4 +5,7 @@
* 2.0.
*/
-export { setup, OverviewTestBed } from './overview.helpers';
+export { setup as setupOverviewPage, OverviewTestBed } from './overview.helpers';
+export { setup as setupIndicesPage, IndicesTestBed } from './indices.helpers';
+
+export { setupEnvironment } from './setup_environment';
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/indices.helpers.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/indices.helpers.ts
new file mode 100644
index 0000000000000..dcedcac27ad32
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/indices.helpers.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest';
+import { PageContent } from '../../public/application/components/page_content';
+import { WithAppDependencies } from './setup_environment';
+
+const testBedConfig: TestBedConfig = {
+ doMountAsync: true,
+};
+
+export type IndicesTestBed = TestBed & {
+ actions: ReturnType;
+};
+
+const createActions = (testBed: TestBed) => {
+ /**
+ * User Actions
+ */
+ const clickTab = (tabName: string) => {
+ const { find } = testBed;
+ const camelcaseTabName = tabName.charAt(0).toUpperCase() + tabName.slice(1);
+
+ find(`upgradeAssistant${camelcaseTabName}Tab`).simulate('click');
+ };
+
+ const clickFixButton = () => {
+ const { find } = testBed;
+ find('removeIndexSettingsButton').simulate('click');
+ };
+
+ const clickExpandAll = () => {
+ const { find } = testBed;
+ find('expandAll').simulate('click');
+ };
+
+ return {
+ clickTab,
+ clickFixButton,
+ clickExpandAll,
+ };
+};
+
+export const setup = async (overrides?: Record): Promise => {
+ const initTestBed = registerTestBed(WithAppDependencies(PageContent, overrides), testBedConfig);
+ const testBed = await initTestBed();
+
+ return {
+ ...testBed,
+ actions: createActions(testBed),
+ };
+};
+
+export type IndicesTestSubjects =
+ | 'expandAll'
+ | 'removeIndexSettingsButton'
+ | 'deprecationsContainer'
+ | string;
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/setup_environment.tsx b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/setup_environment.tsx
index 75a0ce4d47e77..898a7fee15394 100644
--- a/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/setup_environment.tsx
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/helpers/setup_environment.tsx
@@ -7,14 +7,17 @@
import React from 'react';
import axios from 'axios';
+// @ts-ignore
+import axiosXhrAdapter from 'axios/lib/adapters/xhr';
-import { docLinksServiceMock } from '../../../../../src/core/public/mocks';
+import { docLinksServiceMock, notificationServiceMock } from '../../../../../src/core/public/mocks';
import { HttpSetup } from '../../../../../src/core/public';
import { mockKibanaSemverVersion, UA_READONLY_MODE } from '../../common/constants';
import { AppContextProvider } from '../../public/application/app_context';
+import { init as initHttpRequests } from './http_requests';
-const mockHttpClient = axios.create();
+const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });
const contextValue = {
http: (mockHttpClient as unknown) as HttpSetup,
@@ -26,12 +29,25 @@ const contextValue = {
nextMajor: mockKibanaSemverVersion.major + 1,
},
isReadOnlyMode: UA_READONLY_MODE,
+ notifications: notificationServiceMock.createStartContract(),
};
-export const WithAppDependencies = (Comp: any, overrides: any = {}) => (props: any) => {
+export const WithAppDependencies = (
+ Comp: React.FunctionComponent>,
+ overrides: Record = {}
+) => (props: Record) => {
return (
);
};
+
+export const setupEnvironment = () => {
+ const { server, httpRequestsMockHelpers } = initHttpRequests();
+
+ return {
+ server,
+ httpRequestsMockHelpers,
+ };
+};
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/indices.test.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/indices.test.ts
new file mode 100644
index 0000000000000..3eda626c24628
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/indices.test.ts
@@ -0,0 +1,93 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { act } from 'react-dom/test-utils';
+import { indexSettingDeprecations } from '../common/constants';
+import { MIGRATION_DEPRECATION_LEVEL } from '../common/types';
+
+import { IndicesTestBed, setupIndicesPage, setupEnvironment } from './helpers';
+
+describe('Indices tab', () => {
+ let testBed: IndicesTestBed;
+ const { server, httpRequestsMockHelpers } = setupEnvironment();
+
+ const upgradeStatusMockResponse = {
+ readyForUpgrade: false,
+ cluster: [],
+ indices: [
+ {
+ level: 'warning' as MIGRATION_DEPRECATION_LEVEL,
+ message: indexSettingDeprecations.translog.deprecationMessage,
+ url: 'doc_url',
+ index: 'my_index',
+ deprecatedIndexSettings: indexSettingDeprecations.translog.settings,
+ },
+ ],
+ };
+
+ httpRequestsMockHelpers.setLoadStatusResponse(upgradeStatusMockResponse);
+
+ beforeEach(async () => {
+ await act(async () => {
+ testBed = await setupIndicesPage({ isReadOnlyMode: false });
+ });
+
+ // Navigate to the indices tab
+ testBed.actions.clickTab('indices');
+ });
+
+ afterAll(() => {
+ server.restore();
+ });
+
+ describe('Fix indices button', () => {
+ test('removes deprecated index settings', async () => {
+ const { component, actions, exists, find } = testBed;
+
+ expect(exists('deprecationsContainer')).toBe(true);
+
+ // Open all deprecations
+ actions.clickExpandAll();
+
+ const accordionTestSubj = `depgroup_${indexSettingDeprecations.translog.deprecationMessage
+ .split(' ')
+ .join('_')}`;
+
+ await act(async () => {
+ find(`${accordionTestSubj}.removeIndexSettingsButton`).simulate('click');
+ });
+
+ const modal = document.body.querySelector(
+ '[data-test-subj="indexSettingsDeleteConfirmModal"]'
+ );
+ const confirmButton: HTMLButtonElement | null = modal!.querySelector(
+ '[data-test-subj="confirmModalConfirmButton"]'
+ );
+
+ expect(modal).not.toBe(null);
+ expect(modal!.textContent).toContain('Remove deprecated settings');
+
+ const indexName = upgradeStatusMockResponse.indices[0].index;
+
+ httpRequestsMockHelpers.setUpdateIndexSettingsResponse({
+ acknowledged: true,
+ });
+
+ await act(async () => {
+ confirmButton!.click();
+ });
+
+ component.update();
+
+ const request = server.requests[server.requests.length - 1];
+
+ expect(request.method).toBe('POST');
+ expect(request.url).toBe(`/api/upgrade_assistant/${indexName}/index_settings`);
+ expect(request.status).toEqual(200);
+ });
+ });
+});
diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts
index b475c8b89c616..4b41e6ff9d681 100644
--- a/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts
+++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts
@@ -7,14 +7,14 @@
import { act } from 'react-dom/test-utils';
-import { OverviewTestBed, setup } from './helpers';
+import { OverviewTestBed, setupOverviewPage } from './helpers';
-describe(' ', () => {
+describe('Overview page', () => {
let testBed: OverviewTestBed;
beforeEach(async () => {
await act(async () => {
- testBed = await setup();
+ testBed = await setupOverviewPage();
});
});
@@ -32,11 +32,11 @@ describe(' ', () => {
await act(async () => {
// Override the default context value to verify tab content renders as expected
// This will be the default behavior on the last minor before the next major release (e.g., v7.15)
- testBed = await setup({ isReadOnlyMode: false });
+ testBed = await setupOverviewPage({ isReadOnlyMode: false });
});
});
- test('renders the coming soon prompt by default', () => {
+ test('renders the Upgrade Assistant overview tab', () => {
const { exists } = testBed;
expect(exists('comingSoonPrompt')).toBe(false);
diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts b/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts
index c4675adfd1142..7041427fb74d7 100644
--- a/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts
+++ b/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts
@@ -12,24 +12,113 @@ import { reindexOperationWithLargeErrorMessage } from './reindex_operation_with_
export default function ({ getService }: FtrProviderContext) {
const es = getService('es');
+ const supertest = getService('supertest');
- describe('Reindex operation saved object', function () {
- const dotKibanaIndex = '.kibana';
- const fakeSavedObjectId = 'fakeSavedObjectId';
+ describe('Upgrade Assistant', () => {
+ describe('Reindex operation saved object', () => {
+ const dotKibanaIndex = '.kibana';
+ const fakeSavedObjectId = 'fakeSavedObjectId';
- after(async () => {
- // Clean up the fake saved object we created. This will error if the test failed.
- return await es.delete({ index: dotKibanaIndex, id: fakeSavedObjectId });
+ after(async () => {
+ // Clean up the fake saved object we created. This will error if the test failed.
+ return await es.delete({ index: dotKibanaIndex, id: fakeSavedObjectId });
+ });
+
+ it('is indexed successfully with immense error message', async () => {
+ // Guards against regression of https://github.com/elastic/kibana/pull/71710.
+ const result = await es.create({
+ index: dotKibanaIndex, // In normal operation this would be the .kibana-n index.
+ id: fakeSavedObjectId,
+ body: reindexOperationWithLargeErrorMessage,
+ });
+ expect(result).to.be.ok();
+ });
});
- it('is indexed successfully with immense error message', async () => {
- // Guards against regression of https://github.com/elastic/kibana/pull/71710.
- const result = await es.create({
- index: dotKibanaIndex, // In normal operation this would be the .kibana-n index.
- id: fakeSavedObjectId,
- body: reindexOperationWithLargeErrorMessage,
+ describe('Update index settings route', () => {
+ const indexName = 'update_settings_test_index';
+ const indexSettings = {
+ number_of_shards: '3',
+ number_of_replicas: '2',
+ refresh_interval: '1s',
+ };
+
+ before(async () => {
+ // Create an index with settings that can be used for testing
+ try {
+ await es.indices.create({
+ index: indexName,
+ body: {
+ settings: {
+ index: indexSettings,
+ },
+ },
+ });
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.log('[Setup error] Error creating index');
+ throw err;
+ }
+ });
+
+ after(async () => {
+ // Delete index created for test
+ try {
+ await es.indices.delete({
+ index: indexName,
+ });
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.log('[Cleanup error] Error deleting index');
+ throw err;
+ }
+ });
+
+ it('removes index settings', async () => {
+ const { body } = await supertest
+ .post(`/api/upgrade_assistant/${indexName}/index_settings`)
+ .set('kbn-xsrf', 'xxx')
+ .send({
+ settings: ['refresh_interval'], // index setting to remove
+ })
+ .expect(200);
+
+ expect(body).to.eql({
+ acknowledged: true,
+ });
+
+ // Refetch the index and verify settings were updated correctly
+ try {
+ const { body: indexSettingsResponse } = await es.indices.getSettings({
+ index: indexName,
+ });
+
+ const updatedIndexSettings = indexSettingsResponse[indexName].settings.index;
+
+ // Verify number_of_shards and number_of_replicas are unchanged
+ expect(updatedIndexSettings.number_of_shards).to.eql(indexSettings.number_of_shards);
+ expect(updatedIndexSettings.number_of_replicas).to.eql(indexSettings.number_of_replicas);
+ // Verify refresh_interval no longer exists
+ expect(updatedIndexSettings.refresh_interval).to.be.eql(undefined);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.log('[Error] Unable to fetch index and verify index settings');
+ throw err;
+ }
+ });
+
+ it('handles error', async () => {
+ const indexDoesNotExistName = 'index_does_not_exist';
+ const { body } = await supertest
+ .post(`/api/upgrade_assistant/${indexDoesNotExistName}/index_settings`)
+ .set('kbn-xsrf', 'xxx')
+ .send({
+ settings: ['refresh_interval'], // index setting to remove
+ })
+ .expect(500);
+
+ expect(body.error).to.eql('Internal Server Error');
});
- expect(result).to.be.ok();
});
});
}
From f14ac90e7d05e83321bf571ebec62c0213ecd59e Mon Sep 17 00:00:00 2001
From: Ryland Herrick
Date: Mon, 15 Mar 2021 19:44:18 -0500
Subject: [PATCH 02/24] [Security Solution][Detections] Display callout if
users have new ML jobs installed (#94393)
* WIP: Add basic structure of our ML Job callout
* Tests are not implemented
* logic is questionable
* Detections now makes redundant ML API calls
* Fix JSDoc reference
* Move ML Jobs callout to Rules page
As opposed to the more general Detections page.
* Extends callout logic to include installation of any affected jobs
* If old jobs are used with new ECS data, you'll be missing
anomalies/alerts
* If new jobs are used with old ECS data, you'll be missing
anomalies/alerts
* Flesh out our link to ML Job compatibility docs
This page doesn't exist yet; the URI/copy is subject to change.
* ML Job Upgrade -> ML Job Compatibility
This is a more accurate name for the concept since the problem is more
general than presence/absence of an upgrade.
* Add some placeholder copy to get the ball rolling
* Test callout behavior with different API responses
* Prevent fetching ML data when ML popover is opened/closed
We already fetch this data when the component is initially rendered. In
the normal workflow of page load -> open popover, we perform six (6) ML
API calls, 3 of which are redundant.
The one downside of this is that opening/closing the popover will not
refresh data; however, this workflow would previously have resulted in 6
API calls as well.
* Revert "Prevent fetching ML data when ML popover is opened/closed"
This reverts commit 810b78d2b90e90f6fa97046e84c8c7ab28c4c98d.
* Update link to relevant documentation
We're going to add a new section to this existing page, and link
directly to that heading. We should be able to generate whatever anchor
we need here, so choosing one arbitrarily on the assumption that docs
can make it work.
* Update copy from product
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../links_to_docs/links_components.tsx | 7 ++
.../links_to_docs/links_translations.ts | 8 +++
.../ml/hooks/use_installed_security_jobs.ts | 2 +-
.../affected_job_ids.ts | 63 +++++++++++++++++
.../index.test.tsx | 69 +++++++++++++++++++
.../ml_job_compatibility_callout/index.tsx | 35 ++++++++++
.../translations.tsx | 50 ++++++++++++++
.../pages/detection_engine/rules/index.tsx | 2 +
8 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts
create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx
create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx
diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx
index d5a3cfdefe646..82d4c5b5a3e27 100644
--- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx
@@ -22,3 +22,10 @@ export const DetectionsRequirementsLink = () => (
linkText={i18n.DETECTIONS_REQUIREMENTS_LINK_TEXT}
/>
);
+
+export const MlJobCompatibilityLink = () => (
+
+);
diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts
index fc38899e7ff82..a495d95b3cf37 100644
--- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts
@@ -33,3 +33,11 @@ export const DETECTIONS_REQUIREMENTS_LINK_TEXT = i18n.translate(
defaultMessage: 'Detections prerequisites and requirements',
}
);
+
+export const ML_JOB_COMPATIBILITY_LINK_PATH = 'alerts-ui-monitor.html#ml-job-compatibility';
+export const ML_JOB_COMPATIBILITY_LINK_TEXT = i18n.translate(
+ 'xpack.securitySolution.documentationLinks.mlJobCompatibility.text',
+ {
+ defaultMessage: 'ML job compatibility',
+ }
+);
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts
index 605fc6c09a944..7bdad2514c82e 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts
@@ -31,7 +31,7 @@ export interface UseInstalledSecurityJobsReturn {
* necessary (running jobs, etc).
*
* NOTE: If you need to include jobs that are not currently installed, try the
- * {@link useInstalledSecurityJobs} hook.
+ * {@link useSecurityJobs} hook.
*
*/
export const useInstalledSecurityJobs = (): UseInstalledSecurityJobsReturn => {
diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts
new file mode 100644
index 0000000000000..8228a5c314c2d
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+// These are the job IDs of ML jobs that are dependent on specific ECS data. If
+// any of them is installed, we want to notify the user that they potentially
+// have incompatibility between their beats, rules, and jobs.
+// There are four modules of jobs that are affected. However, because the API
+// that returns installed jobs does not include those jobs' modules, hardcoding
+// the IDs from those modules (as found in e.g.
+// x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/manifest.json)
+// allows us to make this determination from a single API call.
+export const affectedJobIds: string[] = [
+ // security_linux module
+ 'v2_rare_process_by_host_linux_ecs',
+ 'v2_linux_rare_metadata_user',
+ 'v2_linux_rare_metadata_process',
+ 'v2_linux_anomalous_user_name_ecs',
+ 'v2_linux_anomalous_process_all_hosts_ecs',
+ 'v2_linux_anomalous_network_port_activity_ecs',
+ // security_windows module
+ 'v2_rare_process_by_host_windows_ecs',
+ 'v2_windows_anomalous_network_activity_ecs',
+ 'v2_windows_anomalous_path_activity_ecs',
+ 'v2_windows_anomalous_process_all_hosts_ecs',
+ 'v2_windows_anomalous_process_creation',
+ 'v2_windows_anomalous_user_name_ecs',
+ 'v2_windows_rare_metadata_process',
+ 'v2_windows_rare_metadata_user',
+ // siem_auditbeat module
+ 'rare_process_by_host_linux_ecs',
+ 'linux_anomalous_network_activity_ecs',
+ 'linux_anomalous_network_port_activity_ecs',
+ 'linux_anomalous_network_service',
+ 'linux_anomalous_network_url_activity_ecs',
+ 'linux_anomalous_process_all_hosts_ecs',
+ 'linux_anomalous_user_name_ecs',
+ 'linux_rare_metadata_process',
+ 'linux_rare_metadata_user',
+ 'linux_rare_user_compiler',
+ 'linux_rare_kernel_module_arguments',
+ 'linux_rare_sudo_user',
+ 'linux_system_user_discovery',
+ 'linux_system_information_discovery',
+ 'linux_system_process_discovery',
+ 'linux_network_connection_discovery',
+ 'linux_network_configuration_discovery',
+ // siem_winlogbeat module
+ 'rare_process_by_host_windows_ecs',
+ 'windows_anomalous_network_activity_ecs',
+ 'windows_anomalous_path_activity_ecs',
+ 'windows_anomalous_process_all_hosts_ecs',
+ 'windows_anomalous_process_creation',
+ 'windows_anomalous_script',
+ 'windows_anomalous_service',
+ 'windows_anomalous_user_name_ecs',
+ 'windows_rare_user_runas_event',
+ 'windows_rare_metadata_process',
+ 'windows_rare_metadata_user',
+];
diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx
new file mode 100644
index 0000000000000..23f6eea435d72
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx
@@ -0,0 +1,69 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { mount } from 'enzyme';
+import React from 'react';
+
+import { TestProviders } from '../../../../common/mock';
+import { useInstalledSecurityJobs } from '../../../../common/components/ml/hooks/use_installed_security_jobs';
+import { MlJobCompatibilityCallout } from './index';
+
+jest.mock('../../../../common/components/ml/hooks/use_installed_security_jobs');
+
+describe('MlJobCompatibilityCallout', () => {
+ it('renders when new affected jobs are installed', () => {
+ (useInstalledSecurityJobs as jest.Mock).mockReturnValue({
+ loading: false,
+ jobs: [{ id: 'v2_linux_rare_metadata_process' }],
+ });
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(true);
+ });
+
+ it('renders when old affected jobs are installed', () => {
+ (useInstalledSecurityJobs as jest.Mock).mockReturnValue({
+ loading: false,
+ jobs: [{ id: 'linux_rare_metadata_process' }],
+ });
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(true);
+ });
+
+ it('does not render if no affected jobs are installed', () => {
+ (useInstalledSecurityJobs as jest.Mock).mockReturnValue({
+ loading: false,
+ jobs: [{ id: 'windows_rare_user_type10_remote_login' }],
+ });
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(false);
+ });
+
+ it('does not render while jobs are loading', () => {
+ (useInstalledSecurityJobs as jest.Mock).mockReturnValue({
+ loading: true,
+ jobs: [],
+ });
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(false);
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx
new file mode 100644
index 0000000000000..ff266cfd859bf
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx
@@ -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 React, { memo } from 'react';
+
+import { CallOutMessage, CallOutSwitcher } from '../../../../common/components/callouts';
+import { useInstalledSecurityJobs } from '../../../../common/components/ml/hooks/use_installed_security_jobs';
+import { affectedJobIds } from './affected_job_ids';
+import * as i18n from './translations';
+
+const mlJobCompatibilityCalloutMessage: CallOutMessage = {
+ type: 'primary',
+ id: 'ml-job-compatibility',
+ title: i18n.ML_JOB_COMPATIBILITY_CALLOUT_TITLE,
+ description: ,
+};
+
+const MlJobCompatibilityCalloutComponent = () => {
+ const { loading, jobs } = useInstalledSecurityJobs();
+ const newJobsInstalled = jobs.some((job) => affectedJobIds.includes(job.id));
+
+ return (
+
+ );
+};
+
+export const MlJobCompatibilityCallout = memo(MlJobCompatibilityCalloutComponent);
diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx
new file mode 100644
index 0000000000000..35ffdb6f7abf8
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { MlJobCompatibilityLink } from '../../../../common/components/links_to_docs';
+
+export const ML_JOB_COMPATIBILITY_CALLOUT_TITLE = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.mlJobCompatibilityCallout.messageTitle',
+ {
+ defaultMessage: 'Your ML jobs may be incompatible with your data sources and/or ML rules',
+ }
+);
+
+export const MlJobCompatibilityCalloutBody = () => (
+
+
+
+ ),
+ docs: (
+
+ ),
+ }}
+ />
+);
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx
index 89cec16851010..57566986e47d2 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx
@@ -36,6 +36,7 @@ import { SecurityPageName } from '../../../../app/types';
import { LinkButton } from '../../../../common/components/links';
import { useFormatUrl } from '../../../../common/components/link_to';
import { NeedAdminForUpdateRulesCallOut } from '../../../components/callouts/need_admin_for_update_callout';
+import { MlJobCompatibilityCallout } from '../../../components/callouts/ml_job_compatibility_callout';
type Func = () => Promise;
@@ -161,6 +162,7 @@ const RulesPageComponent: React.FC = () => {
<>
+
setShowValueListsModal(false)}
From 7349f82facbc7810b4f90b9267e1e2313540d471 Mon Sep 17 00:00:00 2001
From: Oliver Gupte
Date: Mon, 15 Mar 2021 18:35:50 -0700
Subject: [PATCH 03/24] [APM] Hide correlations tabs with license message
(#94494)
* [APM] Hide correlations tabs with license message (#94228)
* removes unused CorrelationsMetricsLicenseCheck component
* Code readability improvements
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/app/correlations/index.tsx | 137 +++++++++---------
1 file changed, 69 insertions(+), 68 deletions(-)
diff --git a/x-pack/plugins/apm/public/components/app/correlations/index.tsx b/x-pack/plugins/apm/public/components/app/correlations/index.tsx
index 464bcf1cde870..e0651edbeb79b 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/correlations/index.tsx
@@ -35,6 +35,7 @@ import {
import { isActivePlatinumLicense } from '../../../../common/license_check';
import { useLicenseContext } from '../../../context/license/use_license_context';
import { LicensePrompt } from '../../shared/LicensePrompt';
+import { IUrlParams } from '../../../context/url_params_context/types';
const latencyTab = {
key: 'latency',
@@ -53,12 +54,23 @@ const errorRateTab = {
const tabs = [latencyTab, errorRateTab];
export function Correlations() {
+ const license = useLicenseContext();
+ const hasActivePlatinumLicense = isActivePlatinumLicense(license);
const { urlParams } = useUrlParams();
const history = useHistory();
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
const [currentTab, setCurrentTab] = useState(latencyTab.key);
const { component: TabContent } =
tabs.find((tab) => tab.key === currentTab) ?? latencyTab;
+ const metric = {
+ app: 'apm' as const,
+ metric: hasActivePlatinumLicense
+ ? 'correlations_flyout_view'
+ : 'correlations_license_prompt',
+ metricType: METRIC_TYPE.COUNT as METRIC_TYPE.COUNT,
+ };
+ useTrackMetric(metric);
+ useTrackMetric({ ...metric, delay: 15000 });
return (
<>
@@ -99,49 +111,38 @@ export function Correlations() {
/>
-
- {tabs.map(({ key, label }) => (
- {
- setCurrentTab(key);
- }}
- >
- {label}
-
- ))}
-
+ {hasActivePlatinumLicense && (
+
+ {tabs.map(({ key, label }) => (
+ {
+ setCurrentTab(key);
+ }}
+ >
+ {label}
+
+ ))}
+
+ )}
-
- {urlParams.kuery ? (
- <>
-
-
- {i18n.translate(
- 'xpack.apm.correlations.filteringByLabel',
- { defaultMessage: 'Filtering by' }
- )}
-
- {urlParams.kuery}
-
-
- {i18n.translate(
- 'xpack.apm.correlations.clearFiltersLabel',
- { defaultMessage: 'Clear' }
- )}
-
-
-
-
- >
- ) : null}
-
- setIsFlyoutVisible(false)} />
-
+ {hasActivePlatinumLicense ? (
+ <>
+
+ setIsFlyoutVisible(false)} />
+ >
+ ) : (
+
+ )}
@@ -150,39 +151,39 @@ export function Correlations() {
);
}
-const CORRELATIONS_TITLE = i18n.translate('xpack.apm.correlations.title', {
- defaultMessage: 'Correlations',
-});
-
-function CorrelationsMetricsLicenseCheck({
- children,
+function Filters({
+ urlParams,
+ history,
}: {
- children: React.ReactNode;
+ urlParams: IUrlParams;
+ history: ReturnType;
}) {
- const license = useLicenseContext();
- const hasActivePlatinumLicense = isActivePlatinumLicense(license);
-
- const metric = {
- app: 'apm' as const,
- metric: hasActivePlatinumLicense
- ? 'correlations_flyout_view'
- : 'correlations_license_prompt',
- metricType: METRIC_TYPE.COUNT as METRIC_TYPE.COUNT,
- };
- useTrackMetric(metric);
- useTrackMetric({ ...metric, delay: 15000 });
+ if (!urlParams.kuery) {
+ return null;
+ }
return (
<>
- {hasActivePlatinumLicense ? (
- children
- ) : (
-
+
+ {i18n.translate('xpack.apm.correlations.filteringByLabel', {
+ defaultMessage: 'Filtering by',
})}
- />
- )}
+
+ {urlParams.kuery}
+
+
+ {i18n.translate('xpack.apm.correlations.clearFiltersLabel', {
+ defaultMessage: 'Clear',
+ })}
+
+
+
+
>
);
}
+
+const CORRELATIONS_TITLE = i18n.translate('xpack.apm.correlations.title', {
+ defaultMessage: 'Correlations',
+});
From d02169e4be23da7f084c38149462e0574aa57602 Mon Sep 17 00:00:00 2001
From: Tyler Smalley
Date: Mon, 15 Mar 2021 20:50:23 -0700
Subject: [PATCH 04/24] [RFC] Bazel (#92758)
Signed-off-by: Tyler Smalley
Co-authored-by: Spencer
Co-authored-by: Jonathan Budzenski
Co-authored-by: Spencer
Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
---
rfcs/text/0015_bazel.md | 309 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 309 insertions(+)
create mode 100644 rfcs/text/0015_bazel.md
diff --git a/rfcs/text/0015_bazel.md b/rfcs/text/0015_bazel.md
new file mode 100644
index 0000000000000..1bdd80e2cbaaf
--- /dev/null
+++ b/rfcs/text/0015_bazel.md
@@ -0,0 +1,309 @@
+- Start Date: 2021-02-24
+- RFC PR: (leave this empty)
+- Kibana Issue: (leave this empty)
+
+# Summary
+
+Adopt Bazel, an open-source build and test tool as the build system for Kibana.
+
+
+# What is Bazel
+
+Bazel is an open-source build and test tool similar to Make, Maven, and Gradle. It uses a human-readable, high-level build language. Bazel supports projects in multiple languages and builds outputs for multiple platforms. Bazel supports large codebases across multiple repositories, and large numbers of users.
+
+Bazel offers the following advantages:
+
+* **High-level build language**. Bazel uses an abstract, human-readable language to describe the build properties of your project at a high semantical level. Unlike other tools, Bazel operates on the concepts of libraries, binaries, scripts, and data sets, shielding you from the complexity of writing individual calls to tools such as compilers and linkers.
+* **Bazel is fast and reliable**. Bazel caches all previously done work and tracks changes to both file content and build commands. This way, Bazel knows when something needs to be rebuilt, and rebuilds only that. To further speed up your builds, you can set up your project to build in a highly parallel and incremental fashion.
+* **Bazel is multi-platform**. Bazel runs on Linux, macOS, and Windows. Bazel can build binaries and deployable packages for multiple platforms, including desktop, server, and mobile, from the same project.
+* **Bazel scales**. Bazel maintains agility while handling builds with 100k+ source files. It works with multiple repositories and user bases in the tens of thousands.
+* **Bazel is extensible**. Many languages are supported, and you can extend Bazel to support any other language or framework.
+
+For more information, please refer to the [Bazel website](https://www.bazel.build/).
+
+
+# Motivation
+
+Kibana has grown substantially over the years and now includes more than 2,100,000 lines of code across 25,000 TypeScript and Javascript files, excluding NPM dependencies. For someone to get Kibana up and running, they rely on five main steps:
+
+### Installation of NPM dependencies
+
+Yarn Package Manager handles the installation of NPM dependencies, and the migration to Bazel will not immediately affect the time this step takes.
+
+### Building packages
+
+The building of [packages](https://github.com/elastic/kibana/tree/master/packages) happens during the bootstrap process initiated by running `yarn kbn bootstrap` and without any cache takes about a minute. Currently, we maintain a single cache item per package, so drastic changes like switching branches frequently results in the worst-case scenario of no-cache being usable.
+
+### Building TypeScript project references
+
+The size of the project and the amount of TypeScript has created scaling issues, resulting in slow project completion and IDE unresponsiveness. To combat this, we have been migrating plugins to use [project references](https://www.typescriptlang.org/docs/handbook/project-references.html) and pre-build them during bootstrap. Currently, this takes over five minutes to complete.
+
+### Building client-side plugins
+
+The [@kbn/optimizer](https://github.com/elastic/kibana/tree/master/packages/kbn-optimizer) package is responsible for building client-side plugins and is initiated during `yarn start`. Without any cache, it takes between three and four minutes, but is highly dependent on the amount of CPU cores available. The caching works similar to packages and requires a rebuild if any files change. Under the hood, this package is managing a set number of workers to run individual Webpack instances. When we first introduced Webpack back in [June of 2015](https://github.com/elastic/kibana/pull/4335), it was responsible for bundling all client-side code within a single process. As the Kibana project continued to grow over time, this Webpack process continued to impact the developer experience. A common theme to address these issues was through reducing the responsibilities of Webpack by separating [SCSS](https://github.com/elastic/kibana/pull/19643) and [vendor code](https://github.com/elastic/kibana/pull/22618). Knowing we would need to continue to scale, one of the new platform’s core objectives was to be able to build each plugin independently. This work paved the way for what we are proposing here and led to the [creation of @kbn/optimizer](https://github.com/elastic/kibana/pull/53976), which improved performance by separating and parallelizing Webpack builds.
+
+### Compiling server-side code
+
+While in development, we rely on [@babel/register](https://babel.dev/docs/en/babel-register) to transpile server-side code during runtime. The use of `@babel/register` results in the compile-time cost being paid when the code is run, mostly during startup or when initiating a unit test. When comparing development startup to production, where the code is pre-compiled, we see startup time taking about twice as long even when the Babel cache exists.
+
+---
+
+These steps cost developers more than ten minutes of their time when updating or changing branches. These times will continue to worsen as the project continues to grow. Instead of making small incremental changes as we have done in the past, like improving caching in a single area, we would like to leverage Bazel, where there are already solutions to many of these problems.
+
+One of the primary advantages Bazel provides is that the builds are hermetic, meaning they are dependent only on a known set of inputs to ensure the builds are reproducible and cacheable. To create these assurances, builds utilize a sandbox environment with only the defined dependencies available. This not only allows for aggressive local caching but the use of remote caching as well. If Bazel determines that a package or plugin needs to be re-built and it's not in the local cache, it will check the remote cache and, if found, will persist locally for subsequent builds. Once the project has completely migrated to Bazel, a developer will only build code they have directly modified or is dependent on those changes. In building packages and plugins, the expected cost for most developers will be downloading the builds from the remote cache for anything changed since their last build.
+
+Building TypeScript reference definitions and using `@babel/register` will be negated by using the TypeScript compiler directly instead of using Babel. Currently, we use Babel for code generation and `tsc` for type check and type declaration output. Additionally, the TypeScript implementation in [rules_nodejs](https://bazelbuild.github.io/rules_nodejs/TypeScript.html) for Bazel handles incremental builds, resulting in faster re-builds.
+
+In addition to the benefits of building code, there are also benefits regarding running unit tests. Developers currently need to understand what unit tests to run to validate changes or rely on waiting for CI, which has a long feedback loop. Since Bazel knows the dependency tree, it will only run unit tests for a package or plugin modified or dependent on those modifications. This optimization helps developers and will significantly reduce the amount of work CI needs to do. On CI, unit tests take 35 minutes to complete, where the average single Jest project takes just twenty seconds.
+
+# Detailed design
+
+## Installation and configuration
+
+To avoid adding Bazel as a dependency that developers need to manage, we will be using a project called Bazelisk to provide that resolution, similar to Gradle Wrapper. The bootstrap command will ensure that the `@bazel/bazelisk` package is installed globally. Two files will exist at the root of the repository, `.bazeliskversion` and `.bazelversion`, to define the required versions of those packages, similar to specifying the Node version today.
+
+
+## Typescript
+
+The [NodeJS](https://bazelbuild.github.io/rules_nodejs/TypeScript.html) rules for Bazel contain two different methods for handling TypeScript; `ts_library` and `ts_project`. We will be using `ts_project`, as it provides a wrapper around `tsc` where `ts_library` is an open-sourced version of the rule used to compile TypeScript at Google. While there are advantages to `ts_library`, it’s very opinionated and hard to migrate an existing project to while also locking us into a specific version of TypeScript. Over time, it’s expected that `ts_project` will catch up to that of `ts_library`.
+
+Bazel maintains a persistent worker which `ts_project` takes advantage of by keeping the AST in memory and providing incremental updates. This should improve the time it takes for changes to be represented.
+
+A Bazel [macro](https://docs.bazel.build/versions/master/skylark/macros.html) will be created to centralize the usage of `ts_project`. The macro will, at minimum, accept a TypeScript configuration file, supply the base `tsconfig.js` file as a source and ensure incremental builds are enabled.
+
+
+## Webpack
+
+A Bazel [macro](https://docs.bazel.build/versions/master/skylark/macros.html) will be created to centralize the usage of Webpack. The macro will, at minimum, accept a configuration file and supply a base `webpack.config.js` file. Currently, all plugins share the same Webpack configuration. Allowing a plugin to provide additional configuration will allow plugins the ability to add loaders without affecting the performance of others.
+
+While running Kibana from source in development, the proxy server will ensure that client-side code for plugins is compiled and available. This is currently handled by the [basePathProxy](https://github.com/elastic/kibana/blob/master/src/core/server/http/base_path_proxy_server.ts), where server restarts and optimizer builds are observed and cause the proxy to pause requests. With Bazel, we will utilize [iBazel](https://github.com/bazelbuild/bazel-watche) to watch for file changes and re-build the plugin targets when necessary. The watcher will emit [events](https://github.com/bazelbuild/bazel-watcher#remote-events) that we will use to block requests and provide feedback to the logs.
+
+While there are a few proofs of concepts for a Webpack 5 Bazel rule, none currently exist which are deemed production-ready. In the meantime, we can use the Webpack CLI directly. One of the main advantages being explored in these rules will be the support for using the Bazel worker to provide incremental builds similar to what `@kbn/optimizer` is doing today.
+
+We are aware there are quite a few alternatives to Webpack, but our plan is to continue using it during the migration. Once all packages have been migrated to Bazel, it will be much easier to test alternatives through changing the targets of a single plugins `BUILD.bazel` file.
+
+
+### Unit Testing
+
+A Bazel macro will be created to centralize the usage of Jest unit testing. The macro will, at minimum, accept a Jest configuration file, add the [Jest preset](https://github.com/elastic/kibana/blob/master/packages/kbn-test/jest-preset.js) and its dependencies as sources, then use the Jest CLI to execute tests.
+
+Developers currently use `yarn test:jest` to efficiently run tests in a given directory without remembering the command or path. This command will continue to work as it does today, but will begin running tests through Bazel for packages or plugins which have been migrated.
+
+CI will have an additional job to run `bazel test //…:jest`. This will run unit tests for any package or plugin modified or dependent on modifications since the last successful CI run on that branch.
+
+When migrating a package or plugin using Jest to Bazel, a `jest` target using our macro will be defined in its `BUILD.bazel` file. The project is then excluded from the root `jest.config.js` file to ensure the tests do not needlessly run multiple times. While we could still use Babel for supporting TypeScript in Jest, there would be advantages to utilizing Bazel to handle compiling TypeScript. Not only would developers immediately receive type checking, but those builds would also be shared with anything else using the target, like the Kibana server or Webpack.
+
+
+## Yarn & Node Version Management
+
+Bazel provides the ability to define the version of Node and Yarn which are used, and once we have fully migrated to Bazel, developers will no longer need to take action when we choose to change versions. The only requirement would be to have a single version of Yarn installed so scripts defined in the `package.json` could be executed.
+
+Example excerpt from `WORKSPACE.bazel`:
+```python
+node_repositories(
+ node_repositories = {
+ "14.15.4-darwin_amd64": ("node-v14.15.4-darwin-x64.tar.gz", "node-v14.15.4-darwin-x64", "6b0e19e5c2601ef97510f7eb4f52cc8ee261ba14cb05f31eb1a41a5043b0304e"),
+ "14.15.4-linux_arm64": ("node-v14.15.4-linux-arm64.tar.xz", "node-v14.15.4-linux-arm64", "b990bd99679158c3164c55a20c2a6677c3d9e9ffdfa0d4a40afe9c9b5e97a96f"),
+ "14.15.4-linux_s390x": ("node-v14.15.4-linux-s390x.tar.xz", "node-v14.15.4-linux-s390x", "29f794d492eccaf0b08e6492f91162447ad95cfefc213fc580a72e29e11501a9"),
+ "14.15.4-linux_amd64": ("node-v14.15.4-linux-x64.tar.xz", "node-v14.15.4-linux-x64", "ed01043751f86bb534d8c70b16ab64c956af88fd35a9506b7e4a68f5b8243d8a"),
+ "14.15.4-windows_amd64": ("node-v14.15.4-win-x64.zip", "node-v14.15.4-win-x64", "b2a0765240f8fbd3ba90a050b8c87069d81db36c9f3745aff7516e833e4d2ed6"),
+ },
+ node_version = "14.15.4",
+ node_urls = [
+ "https://nodejs.org/dist/v{version}/{filename}",
+ ],
+ yarn_repositories = {
+ "1.21.1": ("yarn-v1.21.1.tar.gz", "yarn-v1.21.1", "d1d9f4a0f16f5ed484e814afeb98f39b82d4728c6c8beaafb5abc99c02db6674"),
+ },
+ yarn_version = "1.21.1",
+ yarn_urls = [
+ "https://github.com/yarnpkg/yarn/releases/download/v{version}/{filename}",
+ ],
+ package_json = ["//:package.json"],
+)
+```
+
+## Target outputs
+
+The Kibana project will contain a new `bazel` directory with symlinks to current builds and logs. This directory is not checked in and is covered by gitignore. More details can be found in the Bazel documentation for [output directory layout](https://docs.bazel.build/versions/master/output_directories.html). Keep in mind we specify a [symlink prefix](https://docs.bazel.build/versions/master/user-manual.html#flag--symlink_prefix) of “bazel” to maintain a single directory.
+
+For most, this change will be welcomed as it has been a common complaint that our targets are scattered throughout the repository making it difficult to search without configuring the ignore list.
+
+
+## Preserve Symlinks
+
+Bazel outputs are created in a folder relative to the monorepo at `./bazel`. However, that folder is just a compilation of symlinks that Bazel creates pointing to temporary folders on the local disk. During the migration, we will begin referencing packages within the `bazel/bin` directory. Internally, Yarn will handle this by creating another symlink from within the `node_modules` directory to packages. By default, any import will be based on the location of the file and not the location of the symlink. This causes issues with module resolution since the `node_modules` directory populated by other dependencies will not be within the tree. To resolve this, we will use the node flag `--preserve-symlinks` that will patch the require calls and prevent Node from expanding the symlinks into their real path during the module resolution.
+
+
+## Build Packaging
+
+One of the additional benefits to Bazel is that it is multi-platform. While it runs on Linux, macOS, and Windows, it can build binaries across platforms.
+
+Bazel provides a [pkg](https://github.com/bazelbuild/rules_pkg/tree/main/pkg) rule providing tar, deb, and rpm support. To facilitate cross-platform tar support in the distributable build, we are currently using tar through Node, which is slow. The pkg tar rule will provide an improvement in performance. For deb and RPM builds, Kibana is currently using a Ruby package called [fpm](https://github.com/jordansissel/fpm) created by a former Elastic employee.
+
+For Docker, we currently create the images during the build using Docker then extract the image as a tar to provide the Release Manager which publishes it to our repository. For ARM, we only create a Docker context which Release Manager uses to create the image on ARM hardware. Bazel has a [docker](https://github.com/bazelbuild/rules_docker) rule, which should allow us to cross-build, and do so without actually using Docker.
+
+The current build is fairly procedural and has little caching where subsequent builds take almost as long as the previous. When working on a step later in the build system, one ultimately ends up commenting out previously completed steps to save time when testing. With Bazel, each target consumes sources or dependencies which could be other targets. Conceivably, we will have a target called release, which is dependent on another target for each of the assets in the distribution (Windows zip, Linux 64-bit tar, Darwin tar, RPM 64-bit, Deb 64-bit, Bed Aarch64, etc). Each one of these assets will then depend on the Kibana core and the rest of the plugins. The entire dependency tree for this will be resolved and rebuilt only when necessary.
+
+
+## scripts/*
+
+We decided to use scripts to define and list any command-line utility for the repository. With Bazel, we can still use these entry points, but they will need to consume the code from `bazel/dist` instead of relying on `src/setup_node_env` to provide transpiling using `@babel/register`.
+
+After the entire migration, we should consider using Bazel targets to execute the script, as with it, we can automatically resolve the dependencies and build anything not yet available.
+
+
+## Remote Cache
+
+As mentioned previously, the remote cache is an essential feature of Bazel and something we plan to utilize.
+
+The Node binary is platform-specific, and because it’s used as an input to build the majority of our targets, we will need to write cache for each platform we support in development. A CI job will build and test all Bazel targets for Linux, macOS, and Windows on merge to a tracked branch. It’s important that this job completes as soon as possible to ensure anyone updating with that branch will have cache available. In the future, we will consider allowing pull request jobs to also write to the cache to minimize this race condition.
+
+We have created a proof of concept using persistent storage on Google Cloud and are currently in a trial with [BuildBuddy](https://www.buildbuddy.io/) which provides not only caching but an event viewer, result store, and remote execution of builds. If we decide to move forward with BuildBuddy, we will most likely use their self-hosted solution where we can provide our own GCP infrastructure.
+
+
+## Packages Build Outline
+
+Within Bazel, the packages will have new overall rules:
+
+* It cannot contain build scripts. Every package build will be written using a Bazel `BUILD.bazel` file
+* It cannot have side effects. Every package build should be cacheable and reproducible and can not produce any side effects
+* Each package should define three major public target rules in `BUILD.bazel` files: `build`, `jest`, and a js_library target with the same name of the folder where the package is living.
+* In order to output its targets in the most Bazel friendly way, each package will output its target according to the following folder structure: for node targets, it will be `target_server`, for web target it will be `target_web` and for types, it will be `target_types`.
+
+
+## package.json’s Outline
+
+As a prerequisite for Bazel and for additional benefits outlined in pull-request [#76412](https://github.com/elastic/kibana/issues/76412), the Kibana repository went from using Yarn Workspaces to a single `package.json` defining all dependencies.
+
+One of the benefits Bazel has over Gradle is the support for Node modules. Bazel will manage the dependencies using either the NPM or Yarn Package Manager. When doing this, a `BUILD.bazel` file will be generated for each module allowing for fine-grained control.
+
+# Adoption strategy
+
+The project is broken down into four initial phases, providing improvements along the way.
+
+
+## Phase I - Infrastructure & Packages
+
+In this phase, we set out to provide the necessary infrastructure outlined previously to begin utilizing Bazel and begin doing so by migrating the current 38 packages.
+
+A `BUILD.bazel` file will be added to the root of each package defining a `build` target. This filegroup target will be what we call during the bootstrap phase to build all packages migrated to Bazel. This target is temporary to maintain similar functionality during our transition. In the future, these procedural build steps will be removed in favor of dependency, tree-driven actions where work will only be done if it’s necessary for the given task like running the Kibana server or executing a unit test.
+
+The `@kbn/pm` package was updated in https://github.com/elastic/kibana/pull/89961 to run the new packages build target, invoked by calling `bazel build //packages:build`, before executing the existing legacy package builds.
+
+The build targets will no longer reside within the package themselves and instead will be within the `bazel/bin` directory. To account for this, any defined dependency will need to be updated to reference the new directory (example: `link:bazel/bin/packages/elastic-datemath`). While also in this transition period, the build will need to copy over the packages from `bazel/bin` into the `node_modules` of the build target.
+
+Example package BUILD.bazel for `packages/elastic-datemath`:
+
+```python
+load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm")
+load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
+
+SRCS = [
+ ".npmignore",
+ "index.js",
+ "index.d.ts",
+ "package.json",
+ "readme",
+ "tsconfig.json",
+]
+
+filegroup(
+ name = "src",
+ srcs = glob(SRCS),
+)
+
+js_library(
+ name = "elastic-datemath",
+ srcs = [ ":src" ],
+ deps = [ "@npm//moment" ],
+ package_name = "@elastic/datemath",
+ visibility = ["//visibility:public"],
+)
+
+alias(
+ name = "build",
+ actual = "elastic-datemath",
+ visibility = ["//visibility:public"],
+)
+```
+
+If the package has unit tests, they will need to be migrated which will be invoked with `bazel test` as described in the Unit Testing section.
+
+
+## Phase II - Docs, Developer Experience
+
+Packages were a likely choice for phase 1 for a few reasons; they aren’t often updated and the developer experience is quite lacking making it easy to maintain parity with. In phase 2, we will bring the developer experience of packages to that which developers are accustomed to with plugins. This means re-builds will be automatic when a change occurs as well as giving time to address any developer experience shortcomings which were not foreseen. During this time we will work on overall Bazel documentation as it pertains to the Kibana repository.
+
+
+## Phase III - Core & Plugins
+
+In this phase, we will be migrating each of the 135 plugins over to being built and unit tested using Bazel. During this time, the legacy systems will stay in place and run in parallel with Bazel. Once all plugins have been migrated, we can decommission the legacy systems.
+
+The `BUILD.bazel` files will look similar to that of packages, there will be a target for `web`, `server`, and `jest`. Just like packages, as the Jest unit tests are migrated, they will need to be removed from the root `jest.config.js` file as described in the Unit Testing section.
+
+Plugins are built in a sandbox, so they will no longer be able to use relative imports from one another. For Typescript, relative imports will be replaced with a path reference to the `bazel/bin`.
+
+Static imports across plugins are a concern that would affect the developer experience due to cascading re-builds. For example, if every plugin has static imports from `src/core`, any changes to `src/core` would cause all those plugins to re-build. There are a few options to address this; the first would be to minimize or eliminate these imports. Most plugins are importing types, so we can also ensure that only type-level changes actually trigger a re-build. Additionally, these types of dependencies could be further broken down into smaller packages to reduce the times further this is necessary.
+
+```
+"compilerOptions": {
+ "rootDirs": [
+ ".",
+ "./bazel/out/host/bin/path/to",
+ "./bazel/out/darwin-fastbuild/bin/path/to",
+ "./bazel/out/k8-fastbuild/bin/path/to",
+ "./bazel/out/x64_windows-fastbuild/bin/path/to",
+ "./bazel/out/darwin-dbg/bin/path/to",
+ "./bazel/out/k8-dbg/bin/path/to",
+ "./bazel/out/x64_windows-dbg/bin/path/to",
+ ]
+}
+```
+
+
+## Phase IV - Build Packaging
+
+In this phase, we will be replacing our current build tooling located at `src/dev/build` to use Bazel. A single target of `release` will provide all assets needed by the release manager:
+
+* Windows (zip)
+* Linux 64-bit (tar)
+* Linux aarch64 (tar)
+* RPM 64-bit
+* RPM aarch64
+* Deb 64-bit
+* Deb aarch64
+* Darwin 64-bit (tar)
+* CentOS 64-bit Docker Image & Context (tar)
+* CentOS aarch64 Docker Image & Context (tar)
+* UBI Docker Image & Context (tar)
+* Ironbank Docker Context (tar)
+
+There are a few rules already available provided by Bazel that should be used. In some cases, like tar, they have been re-implemented to ensure the output is hermetic. `rules_pgk` has `pkg_tar`, `pkg_deb`, `pkg_rpm`, and `pkg_zip` to assist with this. `rules_docker` provides the ability to build containers without depending on Docker to be installed and providing the ability to build for other platforms.
+
+While this phase can begin with phase 1, it can not be completed until all packages and plugins have been migrated.
+
+# Drawbacks
+
+With Bazel, all dependencies need to be defined on each package which can become tedious. However, that is also how Bazel is able to provide the level of cache and performance which it does.
+
+Bazel is substantially different from what people in the Javascript community are accustomed to, so teaching might be difficult. For example, in Javascript when you would like to add support for Typescript, you would probably find a package that adds the support to Jest, Webpack, or Babel. However, in Bazel, it works on inputs and outputs. You could still do what was previously described, but it wouldn’t be efficient. Instead, you would have your Typescript code as an input, which would use the Typescript compiler to output Javascript which would be the input to Webpack or Jest. This way, that compile step would only happen once for each of those paths.
+
+It’s also possible there is something better out there for our use, or as some have suggested splitting up our repository into smaller pieces.
+
+
+# Alternatives
+
+Gradle is widely used at Elastic, however, it doesn’t have the NodeJS specific support which Bazel has.
+
+There are other alternatives that seem to have been created by past Google employees who wanted something like Blaze which is the internal tool used at Google before they open-sourced Bazel (an anagram of Blaze). Most of these just didn’t have a large enough community or provide the level of caching and scaling we were looking for.
+
+
+# Adoption strategy
+
+The migration would happen in phases starting with packages, then the build system, then plugins. All steps in the phase can happen gradually and over time.
+
+
+# How we teach this
+
+There will be a lot to teach here, and we have been iterating on a talk which we would give to the entire Kibana team. The Operations team would be available to assist anyone with questions or assistance with Bazel aspects of the build system.
From f3b74b457cb9b56dc922c29a5c3f496f485b40c3 Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger
Date: Tue, 16 Mar 2021 11:41:48 +0100
Subject: [PATCH 05/24] [ML] Transforms: Fixes missing number of transform
nodes and error reporting in stats bar. (#93956)
- Adds a Kibana API endpoint transforms/_nodes
- Adds number of nodes to the stats bar in the transforms list.
- Shows a callout when no transform nodes are available.
- Disable all actions except delete when no transform nodes are available.
- Disables the create button when no transform nodes are available.
---
.../public/doc_links/doc_links_service.ts | 1 +
.../common/api_schemas/transforms.ts | 5 ++
.../common/api_schemas/type_guards.ts | 9 +++
.../transform/public/app/hooks/use_api.ts | 8 +++
.../app/hooks/use_documentation_links.ts | 1 +
.../public/app/hooks/use_get_transforms.ts | 8 ++-
.../lib/authorization/components/common.ts | 10 ++-
.../step_define/step_define_form.tsx | 3 +-
.../action_clone/use_clone_action.tsx | 6 +-
.../action_edit/use_edit_action.tsx | 6 +-
.../action_start/start_action_name.test.tsx | 1 +
.../action_start/start_action_name.tsx | 18 +++--
.../action_start/use_start_action.tsx | 13 ++--
.../create_transform_button.test.tsx | 2 +-
.../create_transform_button.tsx | 16 ++++-
.../transform_list/transform_list.test.tsx | 3 +-
.../transform_list/transform_list.tsx | 36 +++-------
.../transform_list/transforms_stats_bar.tsx | 68 ++++++++++++++++--
.../transform_list/use_actions.test.tsx | 2 +-
.../components/transform_list/use_actions.tsx | 8 ++-
.../transform_list/use_columns.test.tsx | 2 +-
.../components/transform_list/use_columns.tsx | 6 +-
.../transform_management_section.tsx | 40 ++++++++---
.../transform/server/routes/api/transforms.ts | 5 +-
.../routes/api/transforms_nodes.test.ts | 43 +++++++++++
.../server/routes/api/transforms_nodes.ts | 71 +++++++++++++++++++
.../api_integration/apis/transform/index.ts | 1 +
.../apis/transform/transforms_nodes.ts | 48 +++++++++++++
28 files changed, 368 insertions(+), 72 deletions(-)
create mode 100644 x-pack/plugins/transform/server/routes/api/transforms_nodes.test.ts
create mode 100644 x-pack/plugins/transform/server/routes/api/transforms_nodes.ts
create mode 100644 x-pack/test/api_integration/apis/transform/transforms_nodes.ts
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index ee5f50588ff04..6e52245e16bbf 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -129,6 +129,7 @@ export class DocLinksService {
elasticsearch: {
indexModules: `${ELASTICSEARCH_DOCS}index-modules.html`,
mapping: `${ELASTICSEARCH_DOCS}mapping.html`,
+ nodeRoles: `${ELASTICSEARCH_DOCS}modules-node.html#node-roles`,
remoteClusters: `${ELASTICSEARCH_DOCS}modules-remote-clusters.html`,
remoteClustersProxy: `${ELASTICSEARCH_DOCS}modules-remote-clusters.html#proxy-mode`,
remoteClusersProxySettings: `${ELASTICSEARCH_DOCS}modules-remote-clusters.html#remote-cluster-proxy-settings`,
diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts
index 4d25bd74f4e74..fc5c728311f7d 100644
--- a/x-pack/plugins/transform/common/api_schemas/transforms.ts
+++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts
@@ -16,6 +16,11 @@ import type { TransformId, TransformPivotConfig } from '../types/transform';
import { transformStateSchema, runtimeMappingsSchema } from './common';
+// GET transform nodes
+export interface GetTransformNodesResponseSchema {
+ count: number;
+}
+
// GET transforms
export const getTransformsRequestSchema = schema.arrayOf(
schema.object({
diff --git a/x-pack/plugins/transform/common/api_schemas/type_guards.ts b/x-pack/plugins/transform/common/api_schemas/type_guards.ts
index 28eaf9ce2894f..476e2bad853c9 100644
--- a/x-pack/plugins/transform/common/api_schemas/type_guards.ts
+++ b/x-pack/plugins/transform/common/api_schemas/type_guards.ts
@@ -19,6 +19,7 @@ import type { DeleteTransformsResponseSchema } from './delete_transforms';
import type { StartTransformsResponseSchema } from './start_transforms';
import type { StopTransformsResponseSchema } from './stop_transforms';
import type {
+ GetTransformNodesResponseSchema,
GetTransformsResponseSchema,
PostTransformsPreviewResponseSchema,
PutTransformsResponseSchema,
@@ -35,6 +36,14 @@ const isGenericResponseSchema = (arg: any): arg is T => {
);
};
+export const isGetTransformNodesResponseSchema = (
+ arg: unknown
+): arg is GetTransformNodesResponseSchema => {
+ return (
+ isPopulatedObject(arg) && {}.hasOwnProperty.call(arg, 'count') && typeof arg.count === 'number'
+ );
+};
+
export const isGetTransformsResponseSchema = (arg: unknown): arg is GetTransformsResponseSchema => {
return isGenericResponseSchema(arg);
};
diff --git a/x-pack/plugins/transform/public/app/hooks/use_api.ts b/x-pack/plugins/transform/public/app/hooks/use_api.ts
index 7afbc5e403b78..f3c90a688453d 100644
--- a/x-pack/plugins/transform/public/app/hooks/use_api.ts
+++ b/x-pack/plugins/transform/public/app/hooks/use_api.ts
@@ -29,6 +29,7 @@ import type {
StopTransformsResponseSchema,
} from '../../../common/api_schemas/stop_transforms';
import type {
+ GetTransformNodesResponseSchema,
GetTransformsResponseSchema,
PostTransformsPreviewRequestSchema,
PostTransformsPreviewResponseSchema,
@@ -66,6 +67,13 @@ export const useApi = () => {
return useMemo(
() => ({
+ async getTransformNodes(): Promise {
+ try {
+ return await http.get(`${API_BASE_PATH}transforms/_nodes`);
+ } catch (e) {
+ return e;
+ }
+ },
async getTransform(
transformId: TransformId
): Promise {
diff --git a/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts b/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts
index ded14a2c0e69e..030f96315835a 100644
--- a/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts
+++ b/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts
@@ -13,6 +13,7 @@ export const useDocumentationLinks = () => {
return {
esAggsCompositeMissingBucket: deps.docLinks.links.aggs.composite_missing_bucket,
esIndicesCreateIndex: deps.docLinks.links.apis.createIndex,
+ esNodeRoles: deps.docLinks.links.elasticsearch.nodeRoles,
esPluginDocBasePath: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/plugins/${DOC_LINK_VERSION}/`,
esQueryDsl: deps.docLinks.links.query.queryDsl,
esTransform: deps.docLinks.links.transforms.guide,
diff --git a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts
index dbb268b44cfd2..2d3425dfeedca 100644
--- a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts
+++ b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts
@@ -8,6 +8,7 @@
import { HttpFetchError } from 'src/core/public';
import {
+ isGetTransformNodesResponseSchema,
isGetTransformsResponseSchema,
isGetTransformsStatsResponseSchema,
} from '../../../common/api_schemas/type_guards';
@@ -22,6 +23,7 @@ export type GetTransforms = (forceRefresh?: boolean) => void;
export const useGetTransforms = (
setTransforms: React.Dispatch>,
+ setTransformNodes: React.Dispatch>,
setErrorMessage: React.Dispatch>,
setIsInitialized: React.Dispatch>,
blockRefresh: boolean
@@ -40,17 +42,20 @@ export const useGetTransforms = (
}
const fetchOptions = { asSystemRequest: true };
+ const transformNodes = await api.getTransformNodes();
const transformConfigs = await api.getTransforms(fetchOptions);
const transformStats = await api.getTransformsStats(fetchOptions);
if (
!isGetTransformsResponseSchema(transformConfigs) ||
- !isGetTransformsStatsResponseSchema(transformStats)
+ !isGetTransformsStatsResponseSchema(transformStats) ||
+ !isGetTransformNodesResponseSchema(transformNodes)
) {
// An error is followed immediately by setting the state to idle.
// This way we're able to treat ERROR as a one-time-event like REFRESH.
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.ERROR);
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
+ setTransformNodes(0);
setTransforms([]);
setIsInitialized(true);
@@ -86,6 +91,7 @@ export const useGetTransforms = (
return reducedtableRows;
}, [] as TransformListRow[]);
+ setTransformNodes(transformNodes.count);
setTransforms(tableRows);
setErrorMessage(undefined);
setIsInitialized(true);
diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts
index cf82478d94423..28e9f190a9108 100644
--- a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts
+++ b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts
@@ -58,7 +58,9 @@ export const hasPrivilegeFactory = (privileges: Privileges | undefined | null) =
// create the text for button's tooltips if the user
// doesn't have the permission to press that button
-export function createCapabilityFailureMessage(capability: keyof Capabilities) {
+export function createCapabilityFailureMessage(
+ capability: keyof Capabilities | 'noTransformNodes'
+) {
let message = '';
switch (capability) {
@@ -80,6 +82,12 @@ export function createCapabilityFailureMessage(capability: keyof Capabilities) {
defaultMessage: 'You do not have permission to delete transforms.',
});
break;
+
+ case 'noTransformNodes':
+ message = i18n.translate('xpack.transform.capability.noPermission.noTransformNodesTooltip', {
+ defaultMessage: 'There are no transform nodes available.',
+ });
+ break;
}
return i18n.translate('xpack.transform.capability.pleaseContactAdministratorTooltip', {
diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
index 1ddb9aa61045b..39593e7da59f8 100644
--- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
+++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
@@ -191,8 +191,7 @@ export const StepDefineForm: FC = React.memo((props) => {
stepDefineForm.advancedPivotEditor.actions.setAdvancedPivotEditorApplyButtonEnabled(false);
};
- const { esQueryDsl } = useDocumentationLinks();
- const { esTransformPivot } = useDocumentationLinks();
+ const { esQueryDsl, esTransformPivot } = useDocumentationLinks();
const advancedEditorsSidebarWidth = '220px';
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx
index 958b329814b88..6249e77ce31dc 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx
@@ -18,7 +18,7 @@ import { useAppDependencies, useToastNotifications } from '../../../../app_depen
import { cloneActionNameText, CloneActionName } from './clone_action_name';
export type CloneAction = ReturnType;
-export const useCloneAction = (forceDisable: boolean) => {
+export const useCloneAction = (forceDisable: boolean, transformNodes: number) => {
const history = useHistory();
const appDeps = useAppDependencies();
const savedObjectsClient = appDeps.savedObjects.client;
@@ -72,14 +72,14 @@ export const useCloneAction = (forceDisable: boolean) => {
const action: TransformListAction = useMemo(
() => ({
name: (item: TransformListRow) => ,
- enabled: () => canCreateTransform && !forceDisable,
+ enabled: () => canCreateTransform && !forceDisable && transformNodes > 0,
description: cloneActionNameText,
icon: 'copy',
type: 'icon',
onClick: clickHandler,
'data-test-subj': 'transformActionClone',
}),
- [canCreateTransform, forceDisable, clickHandler]
+ [canCreateTransform, forceDisable, clickHandler, transformNodes]
);
return { action };
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx
index 353a7660ac582..b84b309c478fd 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx
@@ -14,7 +14,7 @@ import { AuthorizationContext } from '../../../../lib/authorization';
import { editActionNameText, EditActionName } from './edit_action_name';
-export const useEditAction = (forceDisable: boolean) => {
+export const useEditAction = (forceDisable: boolean, transformNodes: number) => {
const { canCreateTransform } = useContext(AuthorizationContext).capabilities;
const [config, setConfig] = useState();
@@ -28,14 +28,14 @@ export const useEditAction = (forceDisable: boolean) => {
const action: TransformListAction = useMemo(
() => ({
name: () => ,
- enabled: () => canCreateTransform || !forceDisable,
+ enabled: () => canCreateTransform && !forceDisable && transformNodes > 0,
description: editActionNameText,
icon: 'pencil',
type: 'icon',
onClick: (item: TransformListRow) => showFlyout(item.config),
'data-test-subj': 'transformActionEdit',
}),
- [canCreateTransform, forceDisable]
+ [canCreateTransform, forceDisable, transformNodes]
);
return {
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx
index 5559f7758204f..490651afc7e96 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.test.tsx
@@ -23,6 +23,7 @@ describe('Transform: Transform List Actions ', () => {
const props: StartActionNameProps = {
forceDisable: false,
items: [item],
+ transformNodes: 1,
};
const wrapper = shallow( );
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx
index 5cc0ac077c240..32207fc586c82 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx
@@ -26,7 +26,8 @@ export const startActionNameText = i18n.translate(
export const isStartActionDisabled = (
items: TransformListRow[],
- canStartStopTransform: boolean
+ canStartStopTransform: boolean,
+ transformNodes: number
) => {
// Disable start for batch transforms which have completed.
const completedBatchTransform = items.some((i: TransformListRow) => isCompletedBatchTransform(i));
@@ -36,15 +37,24 @@ export const isStartActionDisabled = (
);
return (
- !canStartStopTransform || completedBatchTransform || startedTransform || items.length === 0
+ !canStartStopTransform ||
+ completedBatchTransform ||
+ startedTransform ||
+ items.length === 0 ||
+ transformNodes === 0
);
};
export interface StartActionNameProps {
items: TransformListRow[];
forceDisable?: boolean;
+ transformNodes: number;
}
-export const StartActionName: FC = ({ items, forceDisable }) => {
+export const StartActionName: FC = ({
+ items,
+ forceDisable,
+ transformNodes,
+}) => {
const { canStartStopTransform } = useContext(AuthorizationContext).capabilities;
const isBulkAction = items.length > 1;
@@ -89,7 +99,7 @@ export const StartActionName: FC = ({ items, forceDisable
);
}
- const actionIsDisabled = isStartActionDisabled(items, canStartStopTransform);
+ const actionIsDisabled = isStartActionDisabled(items, canStartStopTransform, transformNodes);
let content: string | undefined;
if (actionIsDisabled && items.length > 0) {
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx
index 02379972ba87c..2c45da45509e5 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx
@@ -16,7 +16,7 @@ import { useStartTransforms } from '../../../../hooks';
import { isStartActionDisabled, startActionNameText, StartActionName } from './start_action_name';
export type StartAction = ReturnType;
-export const useStartAction = (forceDisable: boolean) => {
+export const useStartAction = (forceDisable: boolean, transformNodes: number) => {
const { canStartStopTransform } = useContext(AuthorizationContext).capabilities;
const startTransforms = useStartTransforms();
@@ -43,17 +43,22 @@ export const useStartAction = (forceDisable: boolean) => {
const action: TransformListAction = useMemo(
() => ({
name: (item: TransformListRow) => (
-
+
),
available: (item: TransformListRow) => item.stats.state === TRANSFORM_STATE.STOPPED,
- enabled: (item: TransformListRow) => !isStartActionDisabled([item], canStartStopTransform),
+ enabled: (item: TransformListRow) =>
+ !isStartActionDisabled([item], canStartStopTransform, transformNodes),
description: startActionNameText,
icon: 'play',
type: 'icon',
onClick: (item: TransformListRow) => openModal([item]),
'data-test-subj': 'transformActionStart',
}),
- [canStartStopTransform, forceDisable]
+ [canStartStopTransform, forceDisable, transformNodes]
);
return {
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
index 275585246d82c..0a7324fd09ffc 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
@@ -14,7 +14,7 @@ jest.mock('../../../../../shared_imports');
describe('Transform: Transform List ', () => {
test('Minimal initialization', () => {
- const wrapper = shallow( );
+ const wrapper = shallow( );
expect(wrapper).toMatchSnapshot();
});
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.tsx
index 47addec6c0e5e..96b0b51294f08 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.tsx
@@ -18,15 +18,20 @@ import {
interface CreateTransformButtonProps {
onClick: MouseEventHandler;
+ transformNodes: number;
}
-export const CreateTransformButton: FC = ({ onClick }) => {
+export const CreateTransformButton: FC = ({
+ onClick,
+ transformNodes,
+}) => {
const { capabilities } = useContext(AuthorizationContext);
const disabled =
!capabilities.canCreateTransform ||
!capabilities.canPreviewTransform ||
- !capabilities.canStartStopTransform;
+ !capabilities.canStartStopTransform ||
+ transformNodes === 0;
const createTransformButton = (
= ({ onClick
if (disabled) {
return (
-
+ 0 ? 'canCreateTransform' : 'noTransformNodes'
+ )}
+ >
{createTransformButton}
);
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
index 7665063dce2d8..ac00d31a620b9 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
@@ -17,9 +17,8 @@ describe('Transform: Transform List ', () => {
test('Minimal initialization', () => {
const wrapper = shallow(
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx
index 8668281d0b181..bacf8f9deccae 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx
@@ -12,7 +12,6 @@ import { i18n } from '@kbn/i18n';
import {
EuiButtonEmpty,
EuiButtonIcon,
- EuiCallOut,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
@@ -62,18 +61,16 @@ function getItemIdToExpandedRowMap(
}, {} as ItemIdToExpandedRowMap);
}
-interface Props {
- errorMessage: any;
- isInitialized: boolean;
+interface TransformListProps {
onCreateTransform: MouseEventHandler;
+ transformNodes: number;
transforms: TransformListRow[];
transformsLoading: boolean;
}
-export const TransformList: FC = ({
- errorMessage,
- isInitialized,
+export const TransformList: FC = ({
onCreateTransform,
+ transformNodes,
transforms,
transformsLoading,
}) => {
@@ -86,7 +83,7 @@ export const TransformList: FC = ({
const [expandedRowItemIds, setExpandedRowItemIds] = useState([]);
const [transformSelection, setTransformSelection] = useState([]);
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false);
- const bulkStartAction = useStartAction(false);
+ const bulkStartAction = useStartAction(false, transformNodes);
const bulkDeleteAction = useDeleteAction(false);
const [searchError, setSearchError] = useState(undefined);
@@ -106,6 +103,7 @@ export const TransformList: FC = ({
const { columns, modals: singleActionModals } = useColumns(
expandedRowItemIds,
setExpandedRowItemIds,
+ transformNodes,
transformSelection
);
@@ -131,26 +129,10 @@ export const TransformList: FC = ({
}
};
- // Before the transforms have been loaded for the first time, display the loading indicator only.
- // Otherwise a user would see 'No transforms found' during the initial loading.
- if (!isInitialized) {
+ if (transforms.length === 0 && transformNodes === 0) {
return null;
}
- if (typeof errorMessage !== 'undefined') {
- return (
-
- {JSON.stringify(errorMessage)}
-
- );
- }
-
if (transforms.length === 0) {
return (
= ({
const bulkActionMenuItems = [
bulkStartAction.openModal(transformSelection)}>
-
+
,
@@ -257,7 +239,7 @@ export const TransformList: FC
= ({
-
+
);
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx
index fed9f0d9a8518..16d5cd800b548 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx
@@ -6,15 +6,21 @@
*/
import React, { FC } from 'react';
+
+import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
+
import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants';
import { TransformListRow } from '../../../../common';
+import { useDocumentationLinks } from '../../../../hooks/use_documentation_links';
+
import { StatsBar, TransformStatsBarStats } from '../stats_bar';
-function createTranformStats(transformsList: TransformListRow[]) {
+function createTranformStats(transformNodes: number, transformsList: TransformListRow[]) {
const transformStats = {
total: {
label: i18n.translate('xpack.transform.statsBar.totalTransformsLabel', {
@@ -51,6 +57,13 @@ function createTranformStats(transformsList: TransformListRow[]) {
value: 0,
show: true,
},
+ nodes: {
+ label: i18n.translate('xpack.transform.statsBar.transformNodesLabel', {
+ defaultMessage: 'Nodes',
+ }),
+ value: transformNodes,
+ show: true,
+ },
};
if (transformsList === undefined) {
@@ -87,12 +100,57 @@ function createTranformStats(transformsList: TransformListRow[]) {
return transformStats;
}
-interface Props {
+interface TransformStatsBarProps {
+ transformNodes: number;
transformsList: TransformListRow[];
}
-export const TransformStatsBar: FC = ({ transformsList }) => {
- const transformStats: TransformStatsBarStats = createTranformStats(transformsList);
+export const TransformStatsBar: FC = ({
+ transformNodes,
+ transformsList,
+}) => {
+ const { esNodeRoles } = useDocumentationLinks();
+
+ const transformStats: TransformStatsBarStats = createTranformStats(
+ transformNodes,
+ transformsList
+ );
- return ;
+ return (
+ <>
+
+ {transformNodes === 0 && (
+ <>
+
+
+ }
+ color="warning"
+ iconType="alert"
+ >
+
+
+
+
+ ),
+ }}
+ />
+
+
+ >
+ )}
+ >
+ );
};
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx
index 5b3f921a07d67..90487d21610ea 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx
@@ -14,7 +14,7 @@ jest.mock('../../../../../app/app_dependencies');
describe('Transform: Transform List Actions', () => {
test('useActions()', () => {
- const { result } = renderHook(() => useActions({ forceDisable: false }));
+ const { result } = renderHook(() => useActions({ forceDisable: false, transformNodes: 1 }));
const actions = result.current.actions;
// Using `any` for the callback. Somehow the EUI types don't pass
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx
index b30cbd0aba741..d9b9008490666 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx
@@ -20,16 +20,18 @@ import { useStopAction } from '../action_stop';
export const useActions = ({
forceDisable,
+ transformNodes,
}: {
forceDisable: boolean;
+ transformNodes: number;
}): {
actions: EuiTableActionsColumnType['actions'];
modals: JSX.Element;
} => {
- const cloneAction = useCloneAction(forceDisable);
+ const cloneAction = useCloneAction(forceDisable, transformNodes);
const deleteAction = useDeleteAction(forceDisable);
- const editAction = useEditAction(forceDisable);
- const startAction = useStartAction(forceDisable);
+ const editAction = useEditAction(forceDisable, transformNodes);
+ const startAction = useStartAction(forceDisable, transformNodes);
const stopAction = useStopAction(forceDisable);
return {
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx
index ec65781acc4cc..53eed01f1226d 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx
@@ -14,7 +14,7 @@ jest.mock('../../../../../app/app_dependencies');
describe('Transform: Job List Columns', () => {
test('useColumns()', () => {
- const { result } = renderHook(() => useColumns([], () => {}, []));
+ const { result } = renderHook(() => useColumns([], () => {}, 1, []));
const columns: ReturnType['columns'] = result.current.columns;
expect(columns).toHaveLength(7);
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx
index d792192f58b61..a8f6a9a233c62 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx
@@ -65,9 +65,13 @@ export const getTaskStateBadge = (
export const useColumns = (
expandedRowItemIds: TransformId[],
setExpandedRowItemIds: React.Dispatch>,
+ transformNodes: number,
transformSelection: TransformListRow[]
) => {
- const { actions, modals } = useActions({ forceDisable: transformSelection.length > 0 });
+ const { actions, modals } = useActions({
+ forceDisable: transformSelection.length > 0,
+ transformNodes,
+ });
function toggleDetails(item: TransformListRow) {
const index = expandedRowItemIds.indexOf(item.config.id);
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
index fa1a42b94f0b7..cc4c502f21eb5 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
@@ -7,12 +7,15 @@
import React, { FC, Fragment, useEffect, useState } from 'react';
+import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiButtonEmpty,
+ EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
+ EuiLoadingContent,
EuiModal,
EuiPageContent,
EuiPageContentBody,
@@ -42,10 +45,12 @@ export const TransformManagement: FC = () => {
const [isInitialized, setIsInitialized] = useState(false);
const [blockRefresh, setBlockRefresh] = useState(false);
const [transforms, setTransforms] = useState([]);
+ const [transformNodes, setTransformNodes] = useState(0);
const [errorMessage, setErrorMessage] = useState(undefined);
const getTransforms = useGetTransforms(
setTransforms,
+ setTransformNodes,
setErrorMessage,
setIsInitialized,
blockRefresh
@@ -111,15 +116,32 @@ export const TransformManagement: FC = () => {
-
-
-
+ {!isInitialized && }
+ {isInitialized && (
+ <>
+
+
+ {typeof errorMessage !== 'undefined' && (
+
+ {JSON.stringify(errorMessage)}
+
+ )}
+ {typeof errorMessage === 'undefined' && (
+
+ )}
+ >
+ )}
{isSearchSelectionVisible && (
diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts
index 20961a64da44b..93f5caf7cf5b0 100644
--- a/x-pack/plugins/transform/server/routes/api/transforms.ts
+++ b/x-pack/plugins/transform/server/routes/api/transforms.ts
@@ -58,6 +58,7 @@ import { addBasePath } from '../index';
import { isRequestTimeout, fillResultsWithTimeouts, wrapError, wrapEsError } from './error_utils';
import { registerTransformsAuditMessagesRoutes } from './transforms_audit_messages';
+import { registerTransformNodesRoutes } from './transforms_nodes';
import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns';
import { isLatestTransform } from '../../../common/types/transform';
@@ -175,7 +176,6 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) {
}
})
);
- registerTransformsAuditMessagesRoutes(routeDependencies);
/**
* @apiGroup Transforms
@@ -389,6 +389,9 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) {
}
})
);
+
+ registerTransformsAuditMessagesRoutes(routeDependencies);
+ registerTransformNodesRoutes(routeDependencies);
}
async function getIndexPatternId(
diff --git a/x-pack/plugins/transform/server/routes/api/transforms_nodes.test.ts b/x-pack/plugins/transform/server/routes/api/transforms_nodes.test.ts
new file mode 100644
index 0000000000000..462a4688ad455
--- /dev/null
+++ b/x-pack/plugins/transform/server/routes/api/transforms_nodes.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { isNodes } from './transforms_nodes';
+
+describe('Transform: Nodes API endpoint', () => {
+ test('isNodes()', () => {
+ expect(isNodes(undefined)).toBe(false);
+ expect(isNodes({})).toBe(false);
+ expect(isNodes({ nodeId: {} })).toBe(false);
+ expect(isNodes({ nodeId: { someAttribute: {} } })).toBe(false);
+ expect(isNodes({ nodeId: { attributes: {} } })).toBe(false);
+ expect(
+ isNodes({
+ nodeId1: { attributes: { someAttribute: true } },
+ nodeId2: { someAttribute: 'asdf' },
+ })
+ ).toBe(false);
+
+ // Legacy format based on attributes should return false
+ expect(isNodes({ nodeId: { attributes: { someAttribute: true } } })).toBe(false);
+ expect(
+ isNodes({
+ nodeId1: { attributes: { someAttribute: true } },
+ nodeId2: { attributes: { 'transform.node': 'true' } },
+ })
+ ).toBe(false);
+
+ // Current format based on roles should return true
+ expect(isNodes({ nodeId: { roles: ['master', 'transform'] } })).toBe(true);
+ expect(isNodes({ nodeId: { roles: ['transform'] } })).toBe(true);
+ expect(
+ isNodes({
+ nodeId1: { roles: ['master', 'data'] },
+ nodeId2: { roles: ['transform'] },
+ })
+ ).toBe(true);
+ });
+});
diff --git a/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts
new file mode 100644
index 0000000000000..afdcc93998303
--- /dev/null
+++ b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { isPopulatedObject } from '../../../common/utils/object_utils';
+
+import { RouteDependencies } from '../../types';
+
+import { addBasePath } from '../index';
+
+import { wrapError, wrapEsError } from './error_utils';
+
+const NODE_ROLES = 'roles';
+
+interface NodesAttributes {
+ roles: string[];
+}
+type Nodes = Record;
+
+export const isNodes = (arg: unknown): arg is Nodes => {
+ return (
+ isPopulatedObject(arg) &&
+ Object.values(arg).every(
+ (node) =>
+ isPopulatedObject(node) &&
+ {}.hasOwnProperty.call(node, NODE_ROLES) &&
+ Array.isArray(node.roles)
+ )
+ );
+};
+
+export function registerTransformNodesRoutes({ router, license }: RouteDependencies) {
+ /**
+ * @apiGroup Transform Nodes
+ *
+ * @api {get} /api/transforms/_nodes Transform Nodes
+ * @apiName GetTransformNodes
+ * @apiDescription Get transform nodes
+ */
+ router.get(
+ {
+ path: addBasePath('transforms/_nodes'),
+ validate: false,
+ },
+ license.guardApiRoute(async (ctx, req, res) => {
+ try {
+ const {
+ body: { nodes },
+ } = await ctx.core.elasticsearch.client.asInternalUser.nodes.info({
+ filter_path: `nodes.*.${NODE_ROLES}`,
+ });
+
+ let count = 0;
+ if (isNodes(nodes)) {
+ for (const { roles } of Object.values(nodes)) {
+ if (roles.includes('transform')) {
+ count++;
+ }
+ }
+ }
+
+ return res.ok({ body: { count } });
+ } catch (e) {
+ return res.customError(wrapError(wrapEsError(e)));
+ }
+ })
+ );
+}
diff --git a/x-pack/test/api_integration/apis/transform/index.ts b/x-pack/test/api_integration/apis/transform/index.ts
index efea3d69d2212..d0aa9533c3860 100644
--- a/x-pack/test/api_integration/apis/transform/index.ts
+++ b/x-pack/test/api_integration/apis/transform/index.ts
@@ -32,6 +32,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./start_transforms'));
loadTestFile(require.resolve('./stop_transforms'));
loadTestFile(require.resolve('./transforms'));
+ loadTestFile(require.resolve('./transforms_nodes'));
loadTestFile(require.resolve('./transforms_preview'));
loadTestFile(require.resolve('./transforms_stats'));
loadTestFile(require.resolve('./transforms_update'));
diff --git a/x-pack/test/api_integration/apis/transform/transforms_nodes.ts b/x-pack/test/api_integration/apis/transform/transforms_nodes.ts
new file mode 100644
index 0000000000000..593a1fb8fb723
--- /dev/null
+++ b/x-pack/test/api_integration/apis/transform/transforms_nodes.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+
+import type { GetTransformNodesResponseSchema } from '../../../../plugins/transform/common/api_schemas/transforms';
+import { isGetTransformNodesResponseSchema } from '../../../../plugins/transform/common/api_schemas/type_guards';
+import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api';
+import { USER } from '../../../functional/services/transform/security_common';
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default ({ getService }: FtrProviderContext) => {
+ const supertest = getService('supertestWithoutAuth');
+ const transform = getService('transform');
+
+ const expected = {
+ apiTransformTransformsNodes: {
+ count: 1,
+ },
+ };
+
+ function assertTransformsNodesResponseBody(body: GetTransformNodesResponseSchema) {
+ expect(isGetTransformNodesResponseSchema(body)).to.eql(true);
+
+ expect(body.count).to.eql(expected.apiTransformTransformsNodes.count);
+ }
+
+ describe('/api/transform/transforms/_nodes', function () {
+ it('should return the number of available transform nodes', async () => {
+ const { body } = await supertest
+ .get('/api/transform/transforms/_nodes')
+ .auth(
+ USER.TRANSFORM_POWERUSER,
+ transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER)
+ )
+ .set(COMMON_REQUEST_HEADERS)
+ .send()
+ .expect(200);
+
+ assertTransformsNodesResponseBody(body);
+ });
+ });
+};
From 73a73332ebd3debd56148aad194e8d5438df64ec Mon Sep 17 00:00:00 2001
From: ymao1
Date: Tue, 16 Mar 2021 08:05:01 -0400
Subject: [PATCH 06/24] [Alerting][Docs] Updating glossary with new terminology
(#94447)
* Updating glossary with new terminology
* Updating glossary with new terminology
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
docs/glossary.asciidoc | 44 +++++++++++++++++++++++-------------------
1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/docs/glossary.asciidoc b/docs/glossary.asciidoc
index f86f15b1f0e67..02751ec57a1cf 100644
--- a/docs/glossary.asciidoc
+++ b/docs/glossary.asciidoc
@@ -2,7 +2,7 @@
[[glossary]]
= Glossary
-<> | <> | <> | <> | <> | <> | <> | H | I | J | <> | <> | <> | N | O | <> | <> | R | <> | <> | <> | V | <> | X | Y | Z
+<> | <> | <> | <> | <> | <> | <> | H | I | J | <> | <> | <> | N | O | <> | <> | <> | <> | <> | <> | V | <> | X | Y | Z
[float]
[[a_glos]]
@@ -13,10 +13,10 @@
+
--
// tag::action-def[]
-The alert-specific response that occurs when an alert fires.
-An alert can have multiple actions.
+The rule-specific response that occurs when an alerting rule fires.
+A rule can have multiple actions.
See
-{kibana-ref}/action-types.html[Action and connector types].
+{kibana-ref}/action-types.html[Connectors and actions].
// end::action-def[]
--
@@ -28,20 +28,6 @@ Part of {kib} Stack Management.
See {kibana-ref}/advanced-options.html[Advanced Settings].
// end::advanced-settings-def[]
-[[glossary-alert]] alert ::
-// tag::alert-def[]
-A set of <>, schedules, and <>
-that enable notifications.
-See <>.
-// end::alert-def[]
-
-[[glossary-alerts-and-actions]] Alerts and Actions ::
-// tag::alerts-and-actions-def[]
-A comprehensive view of all your alerts. Enables you to access and
-manage alerts for all {kib} apps from one place.
-See {kibana-ref}/alerting-getting-started.html[Alerts and Actions].
-// end::alerts-and-actions-def[]
-
[[glossary-annotation]] annotation ::
// tag::annotation-def[]
A way to augment a data display with descriptive domain knowledge.
@@ -113,13 +99,13 @@ The cluster location is the weighted centroid for all documents in the grid cell
[[glossary-condition]] condition ::
// tag::condition-def[]
-Specifies the circumstances that must be met to trigger an alert.
+Specifies the circumstances that must be met to trigger an alerting rule.
// end::condition-def[]
[[glossary-connector]] connector ::
// tag::connector-def[]
A configuration that enables integration with an external system (the destination for an action).
-See {kibana-ref}/action-types.html[Action and connector types].
+See {kibana-ref}/action-types.html[Connectors and actions].
// end::connector-def[]
[[glossary-console]] Console ::
@@ -335,6 +321,24 @@ A tool that enables you to inspect and analyze search queries to diagnose and de
See {kibana-ref}/xpack-profiler.html[Query Profiler].
// end::query-profiler-def[]
+[float]
+[[r_glos]]
+== R
+
+[[glossary-rule]] rule ::
+// tag::rule-def[]
+A set of <>, schedules, and <>
+that enable notifications.
+See <>.
+// end::rule-def[]
+
+[[glossary-rules-and-connectors]] Rules and Connectors ::
+// tag::rules-and-connectors-def[]
+A comprehensive view of all your alerting rules. Enables you to access and
+manage rules for all {kib} apps from one place.
+See {kibana-ref}/alerting-getting-started.html[Rules and Connectors].
+// end::rules-and-connectors-def[]
+
[float]
[[s_glos]]
== S
From e830e077d3fe88085b7b488cd0f08c554c8a84ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20St=C3=BCrmer?=
Date: Tue, 16 Mar 2021 13:29:29 +0100
Subject: [PATCH 07/24] [Logs UI] Style improvements for log stream search
strategy (#94560)
---
.../log_entries/log_entries_search_strategy.ts | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts
index bf7e497385f9a..190464ab6d5c1 100644
--- a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts
+++ b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts
@@ -20,7 +20,6 @@ import type {
} from '../../../../../../src/plugins/data/server';
import {
LogSourceColumnConfiguration,
- LogSourceConfigurationProperties,
logSourceFieldColumnConfigurationRT,
} from '../../../common/http_api/log_sources';
import {
@@ -107,7 +106,10 @@ export const logEntriesSearchStrategyProvider = ({
params.size + 1,
configuration.fields.timestamp,
configuration.fields.tiebreaker,
- getRequiredFields(configuration, messageFormattingRules, params.columns),
+ getRequiredFields(
+ params.columns ?? configuration.logColumns,
+ messageFormattingRules
+ ),
params.query,
params.highlightPhrase
),
@@ -131,7 +133,7 @@ export const logEntriesSearchStrategyProvider = ({
.slice(0, request.params.size)
.map(
getLogEntryFromHit(
- request.params.columns ? request.params.columns : configuration.logColumns,
+ request.params.columns ?? configuration.logColumns,
messageFormattingRules
)
);
@@ -257,12 +259,9 @@ function getResponseCursors(entries: LogEntry[]) {
const VIEW_IN_CONTEXT_FIELDS = ['log.file.path', 'host.name', 'container.id'];
const getRequiredFields = (
- configuration: LogSourceConfigurationProperties,
- messageFormattingRules: CompiledLogMessageFormattingRule,
- columnOverrides?: LogSourceColumnConfiguration[]
+ columns: LogSourceColumnConfiguration[],
+ messageFormattingRules: CompiledLogMessageFormattingRule
): string[] => {
- const columns = columnOverrides ? columnOverrides : configuration.logColumns;
-
const fieldsFromColumns = columns.reduce((accumulatedFields, logColumn) => {
if (logSourceFieldColumnConfigurationRT.is(logColumn)) {
return [...accumulatedFields, logColumn.fieldColumn.field];
From 638f166f71e9fbb59bfe286d807aec587696be37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20S=C3=A1nchez?=
Date: Tue, 16 Mar 2021 13:36:06 +0100
Subject: [PATCH 08/24] [SECURITY_SOLUTION] Add helper text on entry input for
trusted applications (#94563)
* Added text help for each entry input option
* Add new unit test
* Fix wrong import on test file
* Change entry variable to readonly. Use it.each instead of a for loop
* Move function inside useMemo since it is only used there
* Remove old commented code
* Update failing test
---
.../condition_entry_input/index.test.tsx | 141 ++++++++++++++++++
.../condition_entry_input/index.tsx | 20 ++-
.../create_trusted_app_form.test.tsx | 6 +-
.../pages/trusted_apps/view/translations.ts | 15 ++
4 files changed, 180 insertions(+), 2 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.test.tsx
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.test.tsx
new file mode 100644
index 0000000000000..4e9ec3a0883a2
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.test.tsx
@@ -0,0 +1,141 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { shallow, mount } from 'enzyme';
+import React from 'react';
+import { keys } from 'lodash';
+import {
+ ConditionEntry,
+ ConditionEntryField,
+ OperatingSystem,
+} from '../../../../../../../common/endpoint/types';
+
+import { ConditionEntryInput } from '.';
+import { EuiSuperSelectProps } from '@elastic/eui';
+
+let onRemoveMock: jest.Mock;
+let onChangeMock: jest.Mock;
+let onVisitedMock: jest.Mock;
+
+const entry: Readonly = {
+ field: ConditionEntryField.HASH,
+ type: 'match',
+ operator: 'included',
+ value: 'trustedApp',
+};
+
+describe('Condition entry input', () => {
+ beforeEach(() => {
+ onRemoveMock = jest.fn();
+ onChangeMock = jest.fn();
+ onVisitedMock = jest.fn();
+ });
+
+ const getElement = (
+ subject: string,
+ os: OperatingSystem = OperatingSystem.WINDOWS,
+ isRemoveDisabled: boolean = false
+ ) => (
+
+ );
+
+ it.each(keys(ConditionEntryField).map((k) => [k]))(
+ 'should call on change for field input with value %s',
+ (field) => {
+ const element = shallow(getElement('testOnChange'));
+ expect(onChangeMock).toHaveBeenCalledTimes(0);
+ element
+ .find('[data-test-subj="testOnChange-field"]')
+ .first()
+ .simulate('change', { target: { value: field } });
+ expect(onChangeMock).toHaveBeenCalledTimes(1);
+ expect(onChangeMock).toHaveBeenCalledWith(
+ {
+ ...entry,
+ field: { target: { value: field } },
+ },
+ entry
+ );
+ }
+ );
+
+ it('should call on remove for field input', () => {
+ const element = mount(getElement('testOnRemove'));
+ expect(onRemoveMock).toHaveBeenCalledTimes(0);
+ element.find('[data-test-subj="testOnRemove-remove"]').first().simulate('click');
+ expect(onRemoveMock).toHaveBeenCalledTimes(1);
+ expect(onRemoveMock).toHaveBeenCalledWith(entry);
+ });
+
+ it('should not be able to call on remove for field input because disabled', () => {
+ const element = mount(getElement('testOnRemove', OperatingSystem.WINDOWS, true));
+ expect(onRemoveMock).toHaveBeenCalledTimes(0);
+ element.find('[data-test-subj="testOnRemove-remove"]').first().simulate('click');
+ expect(onRemoveMock).toHaveBeenCalledTimes(0);
+ });
+
+ it('should call on visited for field input', () => {
+ const element = shallow(getElement('testOnVisited'));
+ expect(onVisitedMock).toHaveBeenCalledTimes(0);
+ element.find('[data-test-subj="testOnVisited-value"]').first().simulate('blur');
+ expect(onVisitedMock).toHaveBeenCalledTimes(1);
+ expect(onVisitedMock).toHaveBeenCalledWith(entry);
+ });
+
+ it('should change value for field input', () => {
+ const element = shallow(getElement('testOnChange'));
+ expect(onChangeMock).toHaveBeenCalledTimes(0);
+ element
+ .find('[data-test-subj="testOnChange-value"]')
+ .first()
+ .simulate('change', { target: { value: 'new value' } });
+ expect(onChangeMock).toHaveBeenCalledTimes(1);
+ expect(onChangeMock).toHaveBeenCalledWith(
+ {
+ ...entry,
+ value: 'new value',
+ },
+ entry
+ );
+ });
+
+ it('should be able to select three options when WINDOWS OS', () => {
+ const element = mount(getElement('testCheckSignatureOption'));
+ const superSelectProps = element
+ .find('[data-test-subj="testCheckSignatureOption-field"]')
+ .first()
+ .props() as EuiSuperSelectProps;
+ expect(superSelectProps.options.length).toBe(3);
+ });
+
+ it('should be able to select two options when LINUX OS', () => {
+ const element = mount(getElement('testCheckSignatureOption', OperatingSystem.LINUX));
+ const superSelectProps = element
+ .find('[data-test-subj="testCheckSignatureOption-field"]')
+ .first()
+ .props() as EuiSuperSelectProps;
+ expect(superSelectProps.options.length).toBe(2);
+ });
+
+ it('should be able to select two options when MAC OS', () => {
+ const element = mount(getElement('testCheckSignatureOption', OperatingSystem.MAC));
+ const superSelectProps = element
+ .find('[data-test-subj="testCheckSignatureOption-field"]')
+ .first()
+ .props() as EuiSuperSelectProps;
+ expect(superSelectProps.options.length).toBe(2);
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx
index 72467cf28ec56..f85f00810bc72 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx
@@ -15,6 +15,7 @@ import {
EuiFormRow,
EuiSuperSelect,
EuiSuperSelectOption,
+ EuiText,
} from '@elastic/eui';
import {
@@ -23,7 +24,12 @@ import {
OperatingSystem,
} from '../../../../../../../common/endpoint/types';
-import { CONDITION_FIELD_TITLE, ENTRY_PROPERTY_TITLES, OPERATOR_TITLE } from '../../translations';
+import {
+ CONDITION_FIELD_DESCRIPTION,
+ CONDITION_FIELD_TITLE,
+ ENTRY_PROPERTY_TITLES,
+ OPERATOR_TITLE,
+} from '../../translations';
const ConditionEntryCell = memo<{
showLabel: boolean;
@@ -75,18 +81,30 @@ export const ConditionEntryInput = memo(
]);
const fieldOptions = useMemo>>(() => {
+ const getDropdownDisplay = (field: ConditionEntryField) => (
+ <>
+ {CONDITION_FIELD_TITLE[field]}
+
+ {CONDITION_FIELD_DESCRIPTION[field]}
+
+ >
+ );
+
return [
{
+ dropdownDisplay: getDropdownDisplay(ConditionEntryField.HASH),
inputDisplay: CONDITION_FIELD_TITLE[ConditionEntryField.HASH],
value: ConditionEntryField.HASH,
},
{
+ dropdownDisplay: getDropdownDisplay(ConditionEntryField.PATH),
inputDisplay: CONDITION_FIELD_TITLE[ConditionEntryField.PATH],
value: ConditionEntryField.PATH,
},
...(os === OperatingSystem.WINDOWS
? [
{
+ dropdownDisplay: getDropdownDisplay(ConditionEntryField.SIGNER),
inputDisplay: CONDITION_FIELD_TITLE[ConditionEntryField.SIGNER],
value: ConditionEntryField.SIGNER,
},
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
index 441847bd88bb9..7d056ae6999e7 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
@@ -165,7 +165,11 @@ describe('When showing the Trusted App Create Form', () => {
'.euiSuperSelect__listbox button.euiSuperSelect__item'
)
).map((button) => button.textContent);
- expect(options).toEqual(['Hash', 'Path', 'Signature']);
+ expect(options).toEqual([
+ 'Hashmd5, sha1, or sha256',
+ 'PathThe full path of the application',
+ 'SignatureThe signer of the application',
+ ]);
});
it('should show the value field as required', () => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
index 3b9db3f8a1c02..b594c355a6983 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
@@ -37,6 +37,21 @@ export const CONDITION_FIELD_TITLE: { [K in ConditionEntryField]: string } = {
),
};
+export const CONDITION_FIELD_DESCRIPTION: { [K in ConditionEntryField]: string } = {
+ [ConditionEntryField.HASH]: i18n.translate(
+ 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.description.hash',
+ { defaultMessage: 'md5, sha1, or sha256' }
+ ),
+ [ConditionEntryField.PATH]: i18n.translate(
+ 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.description.path',
+ { defaultMessage: 'The full path of the application' }
+ ),
+ [ConditionEntryField.SIGNER]: i18n.translate(
+ 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.description.signature',
+ { defaultMessage: 'The signer of the application' }
+ ),
+};
+
export const OPERATOR_TITLE: { [K in ConditionEntry['operator']]: string } = {
included: i18n.translate('xpack.securitySolution.trustedapps.card.operator.includes', {
defaultMessage: 'is',
From 4a83a024336c70f518b6bd3b838eade73c1064ae Mon Sep 17 00:00:00 2001
From: Jean-Louis Leysens
Date: Tue, 16 Mar 2021 13:40:13 +0100
Subject: [PATCH 09/24] [ILM] Hide node allocation notices on Cloud (#94581)
* block node allocation notices on cloud
* added test to check that notices are not showing
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../features/node_allocation.test.ts | 30 +++++------
.../components/index.ts | 2 -
.../components/missing_cloud_tier_callout.tsx | 53 -------------------
.../data_tier_allocation_field.tsx | 27 ++--------
4 files changed, 16 insertions(+), 96 deletions(-)
delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx
diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts
index 832963827663d..e289991780c04 100644
--- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts
+++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts
@@ -365,9 +365,9 @@ describe(' node allocation', () => {
await act(async () => {
testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } });
});
- const { actions, component, exists, find } = testBed;
+ testBed.component.update();
- component.update();
+ const { actions, component, exists, find } = testBed;
await actions.warm.enable(true);
expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy();
@@ -375,35 +375,29 @@ describe(' node allocation', () => {
expect(exists('defaultDataAllocationOption')).toBeTruthy();
expect(exists('customDataAllocationOption')).toBeTruthy();
expect(exists('noneDataAllocationOption')).toBeTruthy();
- // We should not be showing the call-to-action for users to activate data tier in cloud
- expect(exists('cloudDataTierCallout')).toBeFalsy();
// Do not show the call-to-action for users to migrate their cluster to use node roles
expect(find('cloudDataTierCallout').exists()).toBeFalsy();
});
-
- test(`shows cloud notice when cold tier nodes do not exist`, async () => {
+ test('do not show node allocation specific warnings on cloud', async () => {
httpRequestsMockHelpers.setListNodes({
- nodesByAttributes: {},
- nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] },
+ nodesByAttributes: { test: ['123'] },
+ // No nodes with node roles like "data_hot" or "data_warm"
+ nodesByRoles: {},
isUsingDeprecatedDataRoleConfig: false,
});
await act(async () => {
testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } });
});
- const { actions, component, exists, find } = testBed;
+ testBed.component.update();
- component.update();
+ const { actions, component, exists } = testBed;
+ await actions.warm.enable(true);
await actions.cold.enable(true);
expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy();
- expect(exists('cloudMissingTierCallout')).toBeTruthy();
- expect(find('cloudMissingTierCallout').text()).toContain(
- `Edit your Elastic Cloud deployment to set up a cold tier`
- );
-
- // Assert that other notices are not showing
- expect(actions.cold.hasDefaultAllocationNotice()).toBeFalsy();
- expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy();
+ expect(exists('cloudDataTierCallout')).toBeFalsy();
+ expect(exists('defaultAllocationNotice')).toBeFalsy();
+ expect(exists('defaultAllocationWarning')).toBeFalsy();
});
});
});
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts
index dacec1df52e2e..e9c884a42fa93 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts
@@ -17,8 +17,6 @@ export { DefaultAllocationWarning } from './default_allocation_warning';
export { NoNodeAttributesWarning } from './no_node_attributes_warning';
-export { MissingCloudTierCallout } from './missing_cloud_tier_callout';
-
export { CloudDataTierCallout } from './cloud_data_tier_callout';
export { LoadingError } from './loading_error';
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx
deleted file mode 100644
index 09d3135cde469..0000000000000
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-import React, { FunctionComponent } from 'react';
-import { EuiCallOut, EuiLink } from '@elastic/eui';
-
-const geti18nTexts = (tier: 'cold' | 'frozen') => ({
- title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.title', {
- defaultMessage: 'Create a {tier} tier',
- values: { tier },
- }),
- body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.body', {
- defaultMessage: 'Edit your Elastic Cloud deployment to set up a {tier} tier.',
- values: { tier },
- }),
- linkText: i18n.translate(
- 'xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.linkToCloudDeploymentDescription',
- { defaultMessage: 'View cloud deployment' }
- ),
-});
-
-interface Props {
- phase: 'cold' | 'frozen';
- linkToCloudDeployment?: string;
-}
-
-/**
- * A call-to-action for users to activate their cold tier slider to provision cold tier nodes.
- * This may need to be change when we have autoscaling enabled on a cluster because nodes may not
- * yet exist, but will automatically be provisioned.
- */
-export const MissingCloudTierCallout: FunctionComponent = ({
- phase,
- linkToCloudDeployment,
-}) => {
- const i18nTexts = geti18nTexts(phase);
-
- return (
-
- {i18nTexts.body}{' '}
- {Boolean(linkToCloudDeployment) && (
-
- {i18nTexts.linkText}
-
- )}
-
- );
-};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx
index ef0e82063ce20..ffd4e2758ab86 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx
@@ -25,7 +25,6 @@ import {
DefaultAllocationNotice,
DefaultAllocationWarning,
NoNodeAttributesWarning,
- MissingCloudTierCallout,
CloudDataTierCallout,
LoadingError,
} from './components';
@@ -59,10 +58,6 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr
const { nodesByRoles, nodesByAttributes, isUsingDeprecatedDataRoleConfig } = data!;
- const hasDataNodeRoles = Object.keys(nodesByRoles).some((nodeRole) =>
- // match any of the "data_" roles, including data_content.
- nodeRole.trim().startsWith('data_')
- );
const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length);
const isCloudEnabled = cloud?.isCloudEnabled ?? false;
const cloudDeploymentUrl = cloud?.cloudDeploymentUrl;
@@ -71,26 +66,12 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr
switch (allocationType) {
case 'node_roles':
/**
- * We'll drive Cloud users to add a cold or frozen tier to their deployment if there are no nodes with that role.
+ * On cloud most users should be using autoscaling which will provision tiers as they are needed. We do not surface any
+ * of the notices below.
*/
- if (
- isCloudEnabled &&
- !isUsingDeprecatedDataRoleConfig &&
- (phase === 'cold' || phase === 'frozen')
- ) {
- const hasNoNodesWithNodeRole = !nodesByRoles[`data_${phase}` as const]?.length;
-
- if (hasDataNodeRoles && hasNoNodesWithNodeRole) {
- // Tell cloud users they can deploy nodes on cloud.
- return (
- <>
-
-
- >
- );
- }
+ if (isCloudEnabled) {
+ return null;
}
-
/**
* Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting
* stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even
From 6c9cfd4893983917a36bc9f82f79e9eb235cf9eb Mon Sep 17 00:00:00 2001
From: James Rodewig <40268737+jrodewig@users.noreply.github.com>
Date: Tue, 16 Mar 2021 08:45:32 -0400
Subject: [PATCH 10/24] Use documentation link service for Watcher (#93339)
---
.../watcher/public/application/app_context.tsx | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/x-pack/plugins/watcher/public/application/app_context.tsx b/x-pack/plugins/watcher/public/application/app_context.tsx
index 8b1efbc9a1fe5..81fbfa97845a6 100644
--- a/x-pack/plugins/watcher/public/application/app_context.tsx
+++ b/x-pack/plugins/watcher/public/application/app_context.tsx
@@ -16,18 +16,14 @@ interface ContextValue extends Omit {
const AppContext = createContext(null as any);
-// eslint-disable-next-line @typescript-eslint/naming-convention
-const generateDocLinks = ({ ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }: DocLinksStart) => {
- const elasticDocLinkBase = `${ELASTIC_WEBSITE_URL}guide/en/`;
- const esBase = `${elasticDocLinkBase}elasticsearch/reference/${DOC_LINK_VERSION}`;
- const kibanaBase = `${elasticDocLinkBase}kibana/${DOC_LINK_VERSION}`;
- const putWatchApiUrl = `${esBase}/watcher-api-put-watch.html`;
- const executeWatchApiUrl = `${esBase}/watcher-api-execute-watch.html#watcher-api-execute-watch-action-mode`;
- const watcherGettingStartedUrl = `${kibanaBase}/watcher-ui.html`;
+const generateDocLinks = ({ links }: DocLinksStart) => {
+ const putWatchApiUrl = `${links.apis.putWatch}`;
+ const executeWatchApiUrl = `${links.apis.executeWatchActionModes}`;
+ const watcherGettingStartedUrl = `${links.watcher.ui}`;
const watchActionsConfigurationMap = {
- [ACTION_TYPES.SLACK]: `${esBase}/actions-slack.html#configuring-slack`,
- [ACTION_TYPES.PAGERDUTY]: `${esBase}/actions-pagerduty.html#configuring-pagerduty`,
- [ACTION_TYPES.JIRA]: `${esBase}/actions-jira.html#configuring-jira`,
+ [ACTION_TYPES.SLACK]: `${links.watcher.slackAction}`,
+ [ACTION_TYPES.PAGERDUTY]: `${links.watcher.pagerDutyAction}`,
+ [ACTION_TYPES.JIRA]: `${links.watcher.jiraAction}`,
};
return {
From b0fa077e8a8e75d2937a0d53a0905e0a5d26612a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
<55978943+cauemarcondes@users.noreply.github.com>
Date: Tue, 16 Mar 2021 09:12:50 -0400
Subject: [PATCH 11/24] [APM] Adding comparison to Throughput chart, Error rate
chart, and Errors table (#94204)
* adding comparison to throuput chart
* adding comparison to error rate chart
* adding comparison to errors table
* fixing/adding api test
* addressing pr comments
* addressing pr comments
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../get_column.tsx | 16 +-
.../service_overview_errors_table/index.tsx | 97 ++-
.../service_overview_throughput_chart.tsx | 73 +-
.../get_columns.tsx | 5 +-
.../transaction_error_rate_chart/index.tsx | 78 +-
.../get_service_map_service_node_info.ts | 3 +
...rvice_error_group_comparison_statistics.ts | 88 ++-
.../lib/transaction_groups/get_error_rate.ts | 73 +-
x-pack/plugins/apm/server/routes/services.ts | 17 +-
.../plugins/apm/server/routes/transactions.ts | 12 +-
.../error_groups_comparison_statistics.snap | 72 ++
.../error_groups_comparison_statistics.ts | 94 ++-
.../__snapshots__/error_rate.snap | 738 ++++++++++++++++++
.../tests/transactions/error_rate.ts | 175 ++++-
14 files changed, 1432 insertions(+), 109 deletions(-)
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx
index 94913c1678d21..fd1120808db9e 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx
@@ -22,9 +22,11 @@ type ErrorGroupComparisonStatistics = APIReturnType<'GET /api/apm/services/{serv
export function getColumns({
serviceName,
errorGroupComparisonStatistics,
+ comparisonEnabled,
}: {
serviceName: string;
errorGroupComparisonStatistics: ErrorGroupComparisonStatistics;
+ comparisonEnabled?: boolean;
}): Array> {
return [
{
@@ -71,12 +73,17 @@ export function getColumns({
),
width: px(unit * 12),
render: (_, { occurrences, group_id: errorGroupId }) => {
- const timeseries =
- errorGroupComparisonStatistics?.[errorGroupId]?.timeseries;
+ const currentPeriodTimeseries =
+ errorGroupComparisonStatistics?.currentPeriod?.[errorGroupId]
+ ?.timeseries;
+ const previousPeriodTimeseries =
+ errorGroupComparisonStatistics?.previousPeriod?.[errorGroupId]
+ ?.timeseries;
+
return (
);
},
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
index bbd36a4c8df93..d36bee8d6be73 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
@@ -18,14 +18,18 @@ import uuid from 'uuid';
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
+import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { ErrorOverviewLink } from '../../../shared/Links/apm/ErrorOverviewLink';
import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper';
+import { getTimeRangeComparison } from '../../../shared/time_comparison/get_time_range_comparison';
import { ServiceOverviewTableContainer } from '../service_overview_table_container';
import { getColumns } from './get_column';
interface Props {
serviceName: string;
}
+type ErrorGroupPrimaryStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/primary_statistics'>;
+type ErrorGroupComparisonStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/comparison_statistics'>;
type SortDirection = 'asc' | 'desc';
type SortField = 'name' | 'last_seen' | 'occurrences';
@@ -36,14 +40,31 @@ const DEFAULT_SORT = {
field: 'occurrences' as const,
};
-const INITIAL_STATE = {
+const INITIAL_STATE_PRIMARY_STATISTICS: {
+ items: ErrorGroupPrimaryStatistics['error_groups'];
+ totalItems: number;
+ requestId?: string;
+} = {
items: [],
+ totalItems: 0,
requestId: undefined,
};
+const INITIAL_STATE_COMPARISON_STATISTICS: ErrorGroupComparisonStatistics = {
+ currentPeriod: {},
+ previousPeriod: {},
+};
+
export function ServiceOverviewErrorsTable({ serviceName }: Props) {
const {
- urlParams: { environment, kuery, start, end },
+ urlParams: {
+ environment,
+ kuery,
+ start,
+ end,
+ comparisonType,
+ comparisonEnabled,
+ },
} = useUrlParams();
const { transactionType } = useApmServiceContext();
const [tableOptions, setTableOptions] = useState<{
@@ -57,9 +78,16 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
sort: DEFAULT_SORT,
});
+ const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
+ start,
+ end,
+ comparisonType,
+ });
+
const { pageIndex, sort } = tableOptions;
+ const { direction, field } = sort;
- const { data = INITIAL_STATE, status } = useFetcher(
+ const { data = INITIAL_STATE_PRIMARY_STATISTICS, status } = useFetcher(
(callApmApi) => {
if (!start || !end || !transactionType) {
return;
@@ -78,37 +106,43 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
},
},
}).then((response) => {
+ const currentPageErrorGroups = orderBy(
+ response.error_groups,
+ field,
+ direction
+ ).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);
+
return {
requestId: uuid(),
- items: response.error_groups,
+ items: currentPageErrorGroups,
+ totalItems: response.error_groups.length,
};
});
},
- [environment, kuery, start, end, serviceName, transactionType]
+ // comparisonType is listed as dependency even thought it is not used. This is needed to trigger the comparison api when it is changed.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [
+ environment,
+ kuery,
+ start,
+ end,
+ serviceName,
+ transactionType,
+ pageIndex,
+ direction,
+ field,
+ comparisonType,
+ ]
);
- const { requestId, items } = data;
- const currentPageErrorGroups = orderBy(
- items,
- sort.field,
- sort.direction
- ).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);
+ const { requestId, items, totalItems } = data;
- const groupIds = JSON.stringify(
- currentPageErrorGroups.map(({ group_id: groupId }) => groupId).sort()
- );
const {
- data: errorGroupComparisonStatistics,
+ data: errorGroupComparisonStatistics = INITIAL_STATE_COMPARISON_STATISTICS,
status: errorGroupComparisonStatisticsStatus,
} = useFetcher(
(callApmApi) => {
- if (
- requestId &&
- currentPageErrorGroups.length &&
- start &&
- end &&
- transactionType
- ) {
+ if (requestId && items.length && start && end && transactionType) {
return callApmApi({
endpoint:
'GET /api/apm/services/{serviceName}/error_groups/comparison_statistics',
@@ -121,21 +155,26 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
end,
numBuckets: 20,
transactionType,
- groupIds,
+ groupIds: JSON.stringify(
+ items.map(({ group_id: groupId }) => groupId).sort()
+ ),
+ comparisonStart,
+ comparisonEnd,
},
},
});
}
},
- // only fetches agg results when requestId or group ids change
+ // only fetches agg results when requestId changes
// eslint-disable-next-line react-hooks/exhaustive-deps
- [requestId, groupIds],
+ [requestId],
{ preservePreviousData: false }
);
const columns = getColumns({
serviceName,
- errorGroupComparisonStatistics: errorGroupComparisonStatistics ?? {},
+ errorGroupComparisonStatistics,
+ comparisonEnabled,
});
return (
@@ -164,16 +203,16 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
();
const {
- urlParams: { environment, kuery, start, end },
+ urlParams: {
+ environment,
+ kuery,
+ start,
+ end,
+ comparisonEnabled,
+ comparisonType,
+ },
} = useUrlParams();
const { transactionType } = useApmServiceContext();
+ const comparisonChartTheme = getComparisonChartTheme(theme);
+ const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
+ start,
+ end,
+ comparisonType,
+ });
const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
@@ -48,14 +65,49 @@ export function ServiceOverviewThroughputChart({
start,
end,
transactionType,
+ comparisonStart,
+ comparisonEnd,
},
},
});
}
},
- [environment, kuery, serviceName, start, end, transactionType]
+ [
+ environment,
+ kuery,
+ serviceName,
+ start,
+ end,
+ transactionType,
+ comparisonStart,
+ comparisonEnd,
+ ]
);
+ const timeseries = [
+ {
+ data: data.currentPeriod,
+ type: 'linemark',
+ color: theme.eui.euiColorVis0,
+ title: i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', {
+ defaultMessage: 'Throughput',
+ }),
+ },
+ ...(comparisonEnabled
+ ? [
+ {
+ data: data.previousPeriod,
+ type: 'area',
+ color: theme.eui.euiColorLightestShade,
+ title: i18n.translate(
+ 'xpack.apm.serviceOverview.throughtputChart.previousPeriodLabel',
+ { defaultMessage: 'Previous period' }
+ ),
+ },
+ ]
+ : []),
+ ];
+
return (
@@ -70,18 +122,9 @@ export function ServiceOverviewThroughputChart({
height={height}
showAnnotations={false}
fetchStatus={status}
- timeseries={[
- {
- data: data.currentPeriod,
- type: 'linemark',
- color: theme.eui.euiColorVis0,
- title: i18n.translate(
- 'xpack.apm.serviceOverview.throughtputChartTitle',
- { defaultMessage: 'Throughput' }
- ),
- },
- ]}
+ timeseries={timeseries}
yLabelFormat={asTransactionRate}
+ customTheme={comparisonChartTheme}
/>
);
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/get_columns.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/get_columns.tsx
index bff45b5d274c3..d9ca3356d7fd2 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/get_columns.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/get_columns.tsx
@@ -159,14 +159,13 @@ export function getColumns({
transactionGroupComparisonStatistics?.currentPeriod?.[name]?.impact ??
0;
const previousImpact =
- transactionGroupComparisonStatistics?.previousPeriod?.[name]
- ?.impact ?? 0;
+ transactionGroupComparisonStatistics?.previousPeriod?.[name]?.impact;
return (
- {comparisonEnabled && (
+ {comparisonEnabled && previousImpact && (
diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
index b3c38651ea178..fd9435db57bfd 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
@@ -9,12 +9,17 @@ import { EuiPanel, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useParams } from 'react-router-dom';
+import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { asPercent } from '../../../../../common/utils/formatters';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { useTheme } from '../../../../hooks/use_theme';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import { TimeseriesChart } from '../timeseries_chart';
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
+import {
+ getComparisonChartTheme,
+ getTimeRangeComparison,
+} from '../../time_comparison/get_time_range_comparison';
function yLabelFormat(y?: number | null) {
return asPercent(y || 0, 1);
@@ -25,6 +30,21 @@ interface Props {
showAnnotations?: boolean;
}
+type ErrorRate = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/error_rate'>;
+
+const INITIAL_STATE: ErrorRate = {
+ currentPeriod: {
+ noHits: true,
+ transactionErrorRate: [],
+ average: null,
+ },
+ previousPeriod: {
+ noHits: true,
+ transactionErrorRate: [],
+ average: null,
+ },
+};
+
export function TransactionErrorRateChart({
height,
showAnnotations = true,
@@ -32,11 +52,25 @@ export function TransactionErrorRateChart({
const theme = useTheme();
const { serviceName } = useParams<{ serviceName?: string }>();
const {
- urlParams: { environment, kuery, start, end, transactionName },
+ urlParams: {
+ environment,
+ kuery,
+ start,
+ end,
+ transactionName,
+ comparisonEnabled,
+ comparisonType,
+ },
} = useUrlParams();
const { transactionType } = useApmServiceContext();
+ const comparisonChartThem = getComparisonChartTheme(theme);
+ const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
+ start,
+ end,
+ comparisonType,
+ });
- const { data, status } = useFetcher(
+ const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
if (transactionType && serviceName && start && end) {
return callApmApi({
@@ -53,6 +87,8 @@ export function TransactionErrorRateChart({
end,
transactionType,
transactionName,
+ comparisonStart,
+ comparisonEnd,
},
},
});
@@ -66,10 +102,34 @@ export function TransactionErrorRateChart({
end,
transactionType,
transactionName,
+ comparisonStart,
+ comparisonEnd,
]
);
- const errorRates = data?.transactionErrorRate || [];
+ const timeseries = [
+ {
+ data: data.currentPeriod.transactionErrorRate,
+ type: 'linemark',
+ color: theme.eui.euiColorVis7,
+ title: i18n.translate('xpack.apm.errorRate.chart.errorRate', {
+ defaultMessage: 'Error rate (avg.)',
+ }),
+ },
+ ...(comparisonEnabled
+ ? [
+ {
+ data: data.previousPeriod.transactionErrorRate,
+ type: 'area',
+ color: theme.eui.euiColorLightestShade,
+ title: i18n.translate(
+ 'xpack.apm.errorRate.chart.errorRate.previousPeriodLabel',
+ { defaultMessage: 'Previous period' }
+ ),
+ },
+ ]
+ : []),
+ ];
return (
@@ -85,18 +145,10 @@ export function TransactionErrorRateChart({
height={height}
showAnnotations={showAnnotations}
fetchStatus={status}
- timeseries={[
- {
- data: errorRates,
- type: 'linemark',
- color: theme.eui.euiColorVis7,
- title: i18n.translate('xpack.apm.errorRate.chart.errorRate', {
- defaultMessage: 'Error rate (avg.)',
- }),
- },
- ]}
+ timeseries={timeseries}
yLabelFormat={yLabelFormat}
yDomain={{ min: 0, max: 1 }}
+ customTheme={comparisonChartThem}
/>
);
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index a6e7832bf697d..7da3ce772ef5f 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -106,11 +106,14 @@ async function getErrorStats({
searchAggregatedTransactions: boolean;
}) {
return withApmSpan('get_error_rate_for_service_map_node', async () => {
+ const { start, end } = setup;
const { noHits, average } = await getErrorRate({
environment,
setup,
serviceName,
searchAggregatedTransactions,
+ start,
+ end,
});
return { avgErrorRate: noHits ? null : average };
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts
index e33044bff8ffa..b559f55bbe78e 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_comparison_statistics.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
import { keyBy } from 'lodash';
+import { Coordinate } from '../../../../typings/timeseries';
import {
ERROR_GROUP_ID,
SERVICE_NAME,
@@ -16,6 +17,7 @@ import {
rangeQuery,
kqlQuery,
} from '../../../../server/utils/queries';
+import { offsetPreviousPeriodCoordinates } from '../../../utils/offset_previous_period_coordinate';
import { withApmSpan } from '../../../utils/with_apm_span';
import { getBucketSize } from '../../helpers/get_bucket_size';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
@@ -28,19 +30,23 @@ export async function getServiceErrorGroupComparisonStatistics({
transactionType,
groupIds,
environment,
+ start,
+ end,
}: {
kuery?: string;
serviceName: string;
- setup: Setup & SetupTimeRange;
+ setup: Setup;
numBuckets: number;
transactionType: string;
groupIds: string[];
environment?: string;
-}) {
+ start: number;
+ end: number;
+}): Promise> {
return withApmSpan(
'get_service_error_group_comparison_statistics',
async () => {
- const { apmEventClient, start, end } = setup;
+ const { apmEventClient } = setup;
const { intervalString } = getBucketSize({ start, end, numBuckets });
@@ -87,10 +93,10 @@ export async function getServiceErrorGroupComparisonStatistics({
});
if (!timeseriesResponse.aggregations) {
- return {};
+ return [];
}
- const groups = timeseriesResponse.aggregations.error_groups.buckets.map(
+ return timeseriesResponse.aggregations.error_groups.buckets.map(
(bucket) => {
const groupId = bucket.key as string;
return {
@@ -104,8 +110,76 @@ export async function getServiceErrorGroupComparisonStatistics({
};
}
);
-
- return keyBy(groups, 'groupId');
}
);
}
+
+export async function getServiceErrorGroupPeriods({
+ kuery,
+ serviceName,
+ setup,
+ numBuckets,
+ transactionType,
+ groupIds,
+ environment,
+ comparisonStart,
+ comparisonEnd,
+}: {
+ kuery?: string;
+ serviceName: string;
+ setup: Setup & SetupTimeRange;
+ numBuckets: number;
+ transactionType: string;
+ groupIds: string[];
+ environment?: string;
+ comparisonStart?: number;
+ comparisonEnd?: number;
+}) {
+ const { start, end } = setup;
+
+ const commonProps = {
+ environment,
+ kuery,
+ serviceName,
+ setup,
+ numBuckets,
+ transactionType,
+ groupIds,
+ };
+
+ const currentPeriodPromise = getServiceErrorGroupComparisonStatistics({
+ ...commonProps,
+ start,
+ end,
+ });
+
+ const previousPeriodPromise =
+ comparisonStart && comparisonEnd
+ ? getServiceErrorGroupComparisonStatistics({
+ ...commonProps,
+ start: comparisonStart,
+ end: comparisonEnd,
+ })
+ : [];
+
+ const [currentPeriod, previousPeriod] = await Promise.all([
+ currentPeriodPromise,
+ previousPeriodPromise,
+ ]);
+
+ const firtCurrentPeriod = currentPeriod.length ? currentPeriod[0] : undefined;
+
+ return {
+ currentPeriod: keyBy(currentPeriod, 'groupId'),
+ previousPeriod: keyBy(
+ previousPeriod.map((errorRateGroup) => ({
+ ...errorRateGroup,
+ timeseries: offsetPreviousPeriodCoordinates({
+ currentPeriodTimeseries: firtCurrentPeriod?.timeseries,
+ previousPeriodTimeseries: errorRateGroup.timeseries,
+ }),
+ })),
+ 'groupId'
+ ),
+ };
+}
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
index 627086df9d681..ec5dd1308cb7e 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
@@ -31,6 +31,7 @@ import {
getTransactionErrorRateTimeSeries,
} from '../helpers/transaction_error_rate';
import { withApmSpan } from '../../utils/with_apm_span';
+import { offsetPreviousPeriodCoordinates } from '../../utils/offset_previous_period_coordinate';
export async function getErrorRate({
environment,
@@ -40,21 +41,25 @@ export async function getErrorRate({
transactionName,
setup,
searchAggregatedTransactions,
+ start,
+ end,
}: {
environment?: string;
kuery?: string;
serviceName: string;
transactionType?: string;
transactionName?: string;
- setup: Setup & SetupTimeRange;
+ setup: Setup;
searchAggregatedTransactions: boolean;
+ start: number;
+ end: number;
}): Promise<{
noHits: boolean;
transactionErrorRate: Coordinate[];
average: number | null;
}> {
return withApmSpan('get_transaction_group_error_rate', async () => {
- const { start, end, apmEventClient } = setup;
+ const { apmEventClient } = setup;
const transactionNamefilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@@ -129,3 +134,67 @@ export async function getErrorRate({
return { noHits, transactionErrorRate, average };
});
}
+
+export async function getErrorRatePeriods({
+ environment,
+ kuery,
+ serviceName,
+ transactionType,
+ transactionName,
+ setup,
+ searchAggregatedTransactions,
+ comparisonStart,
+ comparisonEnd,
+}: {
+ environment?: string;
+ kuery?: string;
+ serviceName: string;
+ transactionType?: string;
+ transactionName?: string;
+ setup: Setup & SetupTimeRange;
+ searchAggregatedTransactions: boolean;
+ comparisonStart?: number;
+ comparisonEnd?: number;
+}) {
+ const { start, end } = setup;
+ const commonProps = {
+ environment,
+ kuery,
+ serviceName,
+ transactionType,
+ transactionName,
+ setup,
+ searchAggregatedTransactions,
+ };
+
+ const currentPeriodPromise = getErrorRate({ ...commonProps, start, end });
+
+ const previousPeriodPromise =
+ comparisonStart && comparisonEnd
+ ? getErrorRate({
+ ...commonProps,
+ start: comparisonStart,
+ end: comparisonEnd,
+ })
+ : { noHits: true, transactionErrorRate: [], average: null };
+
+ const [currentPeriod, previousPeriod] = await Promise.all([
+ currentPeriodPromise,
+ previousPeriodPromise,
+ ]);
+
+ const firtCurrentPeriod = currentPeriod.transactionErrorRate.length
+ ? currentPeriod.transactionErrorRate
+ : undefined;
+
+ return {
+ currentPeriod,
+ previousPeriod: {
+ ...previousPeriod,
+ transactionErrorRate: offsetPreviousPeriodCoordinates({
+ currentPeriodTimeseries: firtCurrentPeriod,
+ previousPeriodTimeseries: previousPeriod.transactionErrorRate,
+ }),
+ },
+ };
+}
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index a84c8dc274248..bac970416792b 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -17,7 +17,7 @@ import { getServices } from '../lib/services/get_services';
import { getServiceAgentName } from '../lib/services/get_service_agent_name';
import { getServiceDependencies } from '../lib/services/get_service_dependencies';
import { getServiceErrorGroupPrimaryStatistics } from '../lib/services/get_service_error_groups/get_service_error_group_primary_statistics';
-import { getServiceErrorGroupComparisonStatistics } from '../lib/services/get_service_error_groups/get_service_error_group_comparison_statistics';
+import { getServiceErrorGroupPeriods } from '../lib/services/get_service_error_groups/get_service_error_group_comparison_statistics';
import { getServiceInstances } from '../lib/services/get_service_instances';
import { getServiceMetadataDetails } from '../lib/services/get_service_metadata_details';
import { getServiceMetadataIcons } from '../lib/services/get_service_metadata_icons';
@@ -329,6 +329,7 @@ export const serviceErrorGroupsComparisonStatisticsRoute = createRoute({
environmentRt,
kueryRt,
rangeRt,
+ comparisonRangeRt,
t.type({
numBuckets: toNumberRt,
transactionType: t.string,
@@ -342,10 +343,18 @@ export const serviceErrorGroupsComparisonStatisticsRoute = createRoute({
const {
path: { serviceName },
- query: { environment, kuery, numBuckets, transactionType, groupIds },
+ query: {
+ environment,
+ kuery,
+ numBuckets,
+ transactionType,
+ groupIds,
+ comparisonStart,
+ comparisonEnd,
+ },
} = context.params;
- return getServiceErrorGroupComparisonStatistics({
+ return getServiceErrorGroupPeriods({
environment,
kuery,
serviceName,
@@ -353,6 +362,8 @@ export const serviceErrorGroupsComparisonStatisticsRoute = createRoute({
numBuckets,
transactionType,
groupIds,
+ comparisonStart,
+ comparisonEnd,
});
},
});
diff --git a/x-pack/plugins/apm/server/routes/transactions.ts b/x-pack/plugins/apm/server/routes/transactions.ts
index 1571efb373cc9..f3424a252e409 100644
--- a/x-pack/plugins/apm/server/routes/transactions.ts
+++ b/x-pack/plugins/apm/server/routes/transactions.ts
@@ -22,7 +22,7 @@ import { getAnomalySeries } from '../lib/transactions/get_anomaly_data';
import { getLatencyPeriods } from '../lib/transactions/get_latency_charts';
import { getThroughputCharts } from '../lib/transactions/get_throughput_charts';
import { getTransactionGroupList } from '../lib/transaction_groups';
-import { getErrorRate } from '../lib/transaction_groups/get_error_rate';
+import { getErrorRatePeriods } from '../lib/transaction_groups/get_error_rate';
import { createRoute } from './create_route';
import {
comparisonRangeRt,
@@ -380,11 +380,9 @@ export const transactionChartsErrorRateRoute = createRoute({
serviceName: t.string,
}),
query: t.intersection([
- environmentRt,
- kueryRt,
- rangeRt,
t.type({ transactionType: t.string }),
t.partial({ transactionName: t.string }),
+ t.intersection([environmentRt, kueryRt, rangeRt, comparisonRangeRt]),
]),
}),
options: { tags: ['access:apm'] },
@@ -397,13 +395,15 @@ export const transactionChartsErrorRateRoute = createRoute({
kuery,
transactionType,
transactionName,
+ comparisonStart,
+ comparisonEnd,
} = params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- return getErrorRate({
+ return getErrorRatePeriods({
environment,
kuery,
serviceName,
@@ -411,6 +411,8 @@ export const transactionChartsErrorRateRoute = createRoute({
transactionName,
setup,
searchAggregatedTransactions,
+ comparisonStart,
+ comparisonEnd,
});
},
});
diff --git a/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap
index a536a6de67ff3..31bc29a2476ca 100644
--- a/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap
+++ b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap
@@ -131,3 +131,75 @@ Object {
],
}
`;
+
+exports[`APM API tests basic apm_8.0.0 Error groups comparison statistics when data is loaded with previous data returns the correct data returns correct timeseries 1`] = `
+Object {
+ "groupId": "051f95eabf120ebe2f8b0399fe3e54c5",
+ "timeseries": Array [
+ Object {
+ "x": 1607436720000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436780000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436840000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436900000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436960000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437020000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437080000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437140000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437200000,
+ "y": 2,
+ },
+ Object {
+ "x": 1607437260000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437320000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607437380000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437440000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437500000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437560000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437620000,
+ "y": 0,
+ },
+ ],
+}
+`;
diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts
index 4a19efac5a809..821d0515aa808 100644
--- a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts
+++ b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts
@@ -7,6 +7,7 @@
import url from 'url';
import expect from '@kbn/expect';
+import moment from 'moment';
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { registry } from '../../common/registry';
@@ -45,8 +46,9 @@ export default function ApiTest({ getService }: FtrProviderContext) {
},
})
);
+
expect(response.status).to.be(200);
- expect(response.body).to.empty();
+ expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
});
}
);
@@ -72,20 +74,23 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(response.status).to.be(200);
const errorGroupsComparisonStatistics = response.body as ErrorGroupsComparisonStatistics;
- expect(Object.keys(errorGroupsComparisonStatistics).sort()).to.eql(groupIds.sort());
+ expect(Object.keys(errorGroupsComparisonStatistics.currentPeriod).sort()).to.eql(
+ groupIds.sort()
+ );
groupIds.forEach((groupId) => {
- expect(errorGroupsComparisonStatistics[groupId]).not.to.be.empty();
+ expect(errorGroupsComparisonStatistics.currentPeriod[groupId]).not.to.be.empty();
});
- const errorgroupsComparisonStatistics = errorGroupsComparisonStatistics[groupIds[0]];
+ const errorgroupsComparisonStatistics =
+ errorGroupsComparisonStatistics.currentPeriod[groupIds[0]];
expect(
- errorgroupsComparisonStatistics.timeseries.map(({ y }) => isFinite(y)).length
+ errorgroupsComparisonStatistics.timeseries.map(({ y }) => y && isFinite(y)).length
).to.be.greaterThan(0);
expectSnapshot(errorgroupsComparisonStatistics).toMatch();
});
- it('returns an empty list when requested groupIds are not available in the given time range', async () => {
+ it('returns an empty state when requested groupIds are not available in the given time range', async () => {
const response = await supertest.get(
url.format({
pathname: `/api/apm/services/opbeans-java/error_groups/comparison_statistics`,
@@ -100,7 +105,82 @@ export default function ApiTest({ getService }: FtrProviderContext) {
);
expect(response.status).to.be(200);
- expect(response.body).to.empty();
+ expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
+ });
+ }
+ );
+
+ registry.when(
+ 'Error groups comparison statistics when data is loaded with previous data',
+ { config: 'basic', archives: [archiveName] },
+ () => {
+ describe('returns the correct data', async () => {
+ let response: {
+ status: number;
+ body: ErrorGroupsComparisonStatistics;
+ };
+ before(async () => {
+ response = await supertest.get(
+ url.format({
+ pathname: `/api/apm/services/opbeans-java/error_groups/comparison_statistics`,
+ query: {
+ numBuckets: 20,
+ transactionType: 'request',
+ groupIds: JSON.stringify(groupIds),
+ start: moment(end).subtract(15, 'minutes').toISOString(),
+ end,
+ comparisonStart: start,
+ comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
+ },
+ })
+ );
+
+ expect(response.status).to.be(200);
+ });
+
+ it('returns correct timeseries', () => {
+ const errorGroupsComparisonStatistics = response.body as ErrorGroupsComparisonStatistics;
+ const errorgroupsComparisonStatistics =
+ errorGroupsComparisonStatistics.currentPeriod[groupIds[0]];
+ expect(
+ errorgroupsComparisonStatistics.timeseries.map(({ y }) => y && isFinite(y)).length
+ ).to.be.greaterThan(0);
+ expectSnapshot(errorgroupsComparisonStatistics).toMatch();
+ });
+
+ it('matches x-axis on current period and previous period', () => {
+ const errorGroupsComparisonStatistics = response.body as ErrorGroupsComparisonStatistics;
+
+ const currentPeriodItems = Object.values(errorGroupsComparisonStatistics.currentPeriod);
+ const previousPeriodItems = Object.values(errorGroupsComparisonStatistics.previousPeriod);
+
+ const currentPeriodFirstItem = currentPeriodItems[0];
+ const previousPeriodFirstItem = previousPeriodItems[0];
+
+ expect(currentPeriodFirstItem.timeseries.map(({ x }) => x)).to.be.eql(
+ previousPeriodFirstItem.timeseries.map(({ x }) => x)
+ );
+ });
+ });
+
+ it('returns an empty state when requested groupIds are not available in the given time range', async () => {
+ const response = await supertest.get(
+ url.format({
+ pathname: `/api/apm/services/opbeans-java/error_groups/comparison_statistics`,
+ query: {
+ numBuckets: 20,
+ transactionType: 'request',
+ groupIds: JSON.stringify(['foo']),
+ start: moment(end).subtract(15, 'minutes').toISOString(),
+ end,
+ comparisonStart: start,
+ comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
+ },
+ })
+ );
+
+ expect(response.status).to.be(200);
+ expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
});
}
);
diff --git a/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap
index d97d39cda1b8d..7ec68bbc0a9fd 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap
+++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap
@@ -248,3 +248,741 @@ Array [
},
]
`;
+
+exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate with comparison data has the correct error rate 1`] = `
+Array [
+ Object {
+ "x": 1607436770000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436780000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436790000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436800000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436810000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436820000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436830000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436840000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436850000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436860000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607436870000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436880000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436890000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436900000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436910000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436920000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436930000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436940000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436950000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436960000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436970000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436980000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436990000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437000000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437010000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437020000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437030000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437040000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437050000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437060000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437070000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437080000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437090000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437100000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437110000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437120000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437130000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437140000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437150000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437160000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437170000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437180000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437190000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437200000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437210000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437220000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437230000,
+ "y": 0.6,
+ },
+ Object {
+ "x": 1607437240000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437250000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437260000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437270000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437280000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437290000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437300000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437310000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437320000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437330000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437340000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437350000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437360000,
+ "y": 0.5,
+ },
+ Object {
+ "x": 1607437370000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437380000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437390000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437400000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437410000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437420000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437430000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437440000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437450000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437460000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607437470000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437480000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607437490000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437500000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437510000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437520000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437530000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437540000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437550000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437560000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437570000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437580000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437590000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437600000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437610000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437620000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437630000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437640000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437650000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437660000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437670000,
+ "y": null,
+ },
+]
+`;
+
+exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate with comparison data has the correct error rate 2`] = `
+Array [
+ Object {
+ "x": 1607436770000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436780000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436790000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436800000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436810000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436820000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436830000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436840000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436850000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436860000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436870000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436880000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436890000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436900000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436910000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436920000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436930000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607436940000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436950000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436960000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436970000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436980000,
+ "y": null,
+ },
+ Object {
+ "x": 1607436990000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437000000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437010000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437020000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437030000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437040000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437050000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437060000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437070000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437080000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437090000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437100000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437110000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437120000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437130000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437140000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437150000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437160000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437170000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437180000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437190000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437200000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437210000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437220000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437230000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437240000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607437250000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437260000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437270000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437280000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437290000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437300000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437310000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437320000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437330000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437340000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437350000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437360000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437370000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437380000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437390000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437400000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437410000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437420000,
+ "y": 0.25,
+ },
+ Object {
+ "x": 1607437430000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437440000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437450000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437460000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437470000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437480000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437490000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437500000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437510000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437520000,
+ "y": 0.5,
+ },
+ Object {
+ "x": 1607437530000,
+ "y": 0.2,
+ },
+ Object {
+ "x": 1607437540000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437550000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437560000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437570000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437580000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437590000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437600000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437610000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437620000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437630000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437640000,
+ "y": null,
+ },
+ Object {
+ "x": 1607437650000,
+ "y": 1,
+ },
+ Object {
+ "x": 1607437660000,
+ "y": 0,
+ },
+ Object {
+ "x": 1607437670000,
+ "y": null,
+ },
+]
+`;
diff --git a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts
index 2b94816466aa7..ce16ad2c96c3b 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts
+++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts
@@ -8,10 +8,14 @@
import expect from '@kbn/expect';
import { first, last } from 'lodash';
import { format } from 'url';
+import moment from 'moment';
+import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { registry } from '../../common/registry';
+type ErrorRate = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/error_rate'>;
+
export default function ApiTest({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
@@ -21,20 +25,43 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const { start, end } = archives_metadata[archiveName];
const transactionType = 'request';
- const url = format({
- pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
- query: { start, end, transactionType },
- });
-
registry.when('Error rate when data is not loaded', { config: 'basic', archives: [] }, () => {
it('handles the empty state', async () => {
- const response = await supertest.get(url);
+ const response = await supertest.get(
+ format({
+ pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
+ query: { start, end, transactionType },
+ })
+ );
expect(response.status).to.be(200);
- expect(response.body.noHits).to.be(true);
+ const body = response.body as ErrorRate;
+ expect(body).to.be.eql({
+ currentPeriod: { noHits: true, transactionErrorRate: [], average: null },
+ previousPeriod: { noHits: true, transactionErrorRate: [], average: null },
+ });
+ });
+
+ it('handles the empty state with comparison data', async () => {
+ const response = await supertest.get(
+ format({
+ pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
+ query: {
+ transactionType,
+ start: moment(end).subtract(15, 'minutes').toISOString(),
+ end,
+ comparisonStart: start,
+ comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
+ },
+ })
+ );
+ expect(response.status).to.be(200);
- expect(response.body.transactionErrorRate.length).to.be(0);
- expect(response.body.average).to.be(null);
+ const body = response.body as ErrorRate;
+ expect(body).to.be.eql({
+ currentPeriod: { noHits: true, transactionErrorRate: [], average: null },
+ previousPeriod: { noHits: true, transactionErrorRate: [], average: null },
+ });
});
});
@@ -43,22 +70,26 @@ export default function ApiTest({ getService }: FtrProviderContext) {
{ config: 'basic', archives: [archiveName] },
() => {
describe('returns the transaction error rate', () => {
- let errorRateResponse: {
- transactionErrorRate: Array<{ x: number; y: number | null }>;
- average: number;
- };
+ let errorRateResponse: ErrorRate;
before(async () => {
- const response = await supertest.get(url);
+ const response = await supertest.get(
+ format({
+ pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
+ query: { start, end, transactionType },
+ })
+ );
errorRateResponse = response.body;
});
it('returns some data', () => {
- expect(errorRateResponse.average).to.be.greaterThan(0);
+ expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
+ expect(errorRateResponse.previousPeriod.average).to.be(null);
- expect(errorRateResponse.transactionErrorRate.length).to.be.greaterThan(0);
+ expect(errorRateResponse.currentPeriod.transactionErrorRate.length).to.be.greaterThan(0);
+ expect(errorRateResponse.previousPeriod.transactionErrorRate).to.empty();
- const nonNullDataPoints = errorRateResponse.transactionErrorRate.filter(
+ const nonNullDataPoints = errorRateResponse.currentPeriod.transactionErrorRate.filter(
({ y }) => y !== null
);
@@ -67,26 +98,126 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('has the correct start date', () => {
expectSnapshot(
- new Date(first(errorRateResponse.transactionErrorRate)?.x ?? NaN).toISOString()
+ new Date(
+ first(errorRateResponse.currentPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
).toMatchInline(`"2020-12-08T13:57:30.000Z"`);
});
it('has the correct end date', () => {
expectSnapshot(
- new Date(last(errorRateResponse.transactionErrorRate)?.x ?? NaN).toISOString()
+ new Date(
+ last(errorRateResponse.currentPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
).toMatchInline(`"2020-12-08T14:27:30.000Z"`);
});
it('has the correct number of buckets', () => {
- expectSnapshot(errorRateResponse.transactionErrorRate.length).toMatchInline(`61`);
+ expectSnapshot(errorRateResponse.currentPeriod.transactionErrorRate.length).toMatchInline(
+ `61`
+ );
+ });
+
+ it('has the correct calculation for average', () => {
+ expectSnapshot(errorRateResponse.currentPeriod.average).toMatchInline(`0.16`);
+ });
+
+ it('has the correct error rate', () => {
+ expectSnapshot(errorRateResponse.currentPeriod.transactionErrorRate).toMatch();
+ });
+ });
+
+ describe('returns the transaction error rate with comparison data', () => {
+ let errorRateResponse: ErrorRate;
+
+ before(async () => {
+ const response = await supertest.get(
+ format({
+ pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
+ query: {
+ transactionType,
+ start: moment(end).subtract(15, 'minutes').toISOString(),
+ end,
+ comparisonStart: start,
+ comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
+ },
+ })
+ );
+ errorRateResponse = response.body;
+ });
+
+ it('returns some data', () => {
+ expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
+ expect(errorRateResponse.previousPeriod.average).to.be.greaterThan(0);
+
+ expect(errorRateResponse.currentPeriod.transactionErrorRate.length).to.be.greaterThan(0);
+ expect(errorRateResponse.previousPeriod.transactionErrorRate.length).to.be.greaterThan(0);
+
+ const currentPeriodNonNullDataPoints = errorRateResponse.currentPeriod.transactionErrorRate.filter(
+ ({ y }) => y !== null
+ );
+
+ const previousPeriodNonNullDataPoints = errorRateResponse.previousPeriod.transactionErrorRate.filter(
+ ({ y }) => y !== null
+ );
+
+ expect(currentPeriodNonNullDataPoints.length).to.be.greaterThan(0);
+ expect(previousPeriodNonNullDataPoints.length).to.be.greaterThan(0);
+ });
+
+ it('has the correct start date', () => {
+ expectSnapshot(
+ new Date(
+ first(errorRateResponse.currentPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
+ ).toMatchInline(`"2020-12-08T14:12:50.000Z"`);
+ expectSnapshot(
+ new Date(
+ first(errorRateResponse.previousPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
+ ).toMatchInline(`"2020-12-08T14:12:50.000Z"`);
+ });
+
+ it('has the correct end date', () => {
+ expectSnapshot(
+ new Date(
+ last(errorRateResponse.currentPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
+ ).toMatchInline(`"2020-12-08T14:27:50.000Z"`);
+ expectSnapshot(
+ new Date(
+ last(errorRateResponse.previousPeriod.transactionErrorRate)?.x ?? NaN
+ ).toISOString()
+ ).toMatchInline(`"2020-12-08T14:27:50.000Z"`);
+ });
+
+ it('has the correct number of buckets', () => {
+ expectSnapshot(errorRateResponse.currentPeriod.transactionErrorRate.length).toMatchInline(
+ `91`
+ );
+ expectSnapshot(
+ errorRateResponse.previousPeriod.transactionErrorRate.length
+ ).toMatchInline(`91`);
});
it('has the correct calculation for average', () => {
- expectSnapshot(errorRateResponse.average).toMatchInline(`0.16`);
+ expectSnapshot(errorRateResponse.currentPeriod.average).toMatchInline(
+ `0.233333333333333`
+ );
+ expectSnapshot(errorRateResponse.previousPeriod.average).toMatchInline(
+ `0.111111111111111`
+ );
});
it('has the correct error rate', () => {
- expectSnapshot(errorRateResponse.transactionErrorRate).toMatch();
+ expectSnapshot(errorRateResponse.currentPeriod.transactionErrorRate).toMatch();
+ expectSnapshot(errorRateResponse.previousPeriod.transactionErrorRate).toMatch();
+ });
+
+ it('matches x-axis on current period and previous period', () => {
+ expect(errorRateResponse.currentPeriod.transactionErrorRate.map(({ x }) => x)).to.be.eql(
+ errorRateResponse.previousPeriod.transactionErrorRate.map(({ x }) => x)
+ );
});
});
}
From b71c6092c9316135131ae2e66ff175a0651c6f86 Mon Sep 17 00:00:00 2001
From: Gabriel Landau <42078554+gabriellandau@users.noreply.github.com>
Date: Tue, 16 Mar 2021 10:00:54 -0400
Subject: [PATCH 12/24] Add bytes_compressed_present to Endpoint Security
Telemetry (#94594)
---
x-pack/plugins/security_solution/server/lib/telemetry/sender.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts
index e169c036419c5..114cf5d2d3425 100644
--- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts
+++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts
@@ -407,6 +407,7 @@ const allowlistEventFields: AllowlistFields = {
bytes_address: true,
bytes_allocation_offset: true,
bytes_compressed: true,
+ bytes_compressed_present: true,
mapped_pe: {
Ext: {
code_signature: {
From ee84e0b0b709d01c08ae42daa08b20c197f7b2a7 Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Tue, 16 Mar 2021 15:13:49 +0100
Subject: [PATCH 13/24] Merge tsconfig and x-pack/tsconfig files (#94519)
* merge all the typings at root level
* merge x-pack/tsconfig into tsconfig.json
* fix tsconfig after changes in master
* remove unnecessary typings
* update paths to the global typings
* update paths to the global elaticsearch typings
* fix import
* fix path to typings/elasticsearch in fleet plugin
* remove file deleted from master
* fix lint errors
---
src/dev/typescript/projects.ts | 1 -
test/tsconfig.json | 5 +-
test/typings/rison_node.d.ts | 28 -----
tsconfig.json | 81 +++++++++++-
.../cytoscape_dagre.d.ts | 12 +-
.../elasticsearch/aggregations.d.ts | 5 +-
.../elasticsearch/index.d.ts | 5 +-
{x-pack/typings => typings}/global_fetch.d.ts | 5 +-
typings/index.d.ts | 2 +
.../typings => typings}/js_levenshtein.d.ts | 5 +-
{x-pack/typings => typings}/react_vis.d.ts | 5 +-
.../examples/alerting_example/tsconfig.json | 2 +-
.../embedded_lens_example/tsconfig.json | 2 +-
.../tsconfig.json | 2 +-
.../shared/KueryBar/get_bool_filter.ts | 2 +-
.../plugins/apm/public/utils/testHelpers.tsx | 2 +-
.../apm/scripts/shared/get_es_client.ts | 2 +-
.../server/lib/alerts/alerting_es_client.ts | 2 +-
.../chart_preview/get_transaction_duration.ts | 2 +-
.../collect_data_telemetry/index.ts | 2 +-
.../collect_data_telemetry/tasks.ts | 2 +-
.../index.ts | 4 +-
.../get_duration_for_percentile.ts | 2 +-
.../get_latency_distribution.ts | 4 +-
.../get_max_latency.ts | 2 +-
.../index.ts | 4 +-
.../process_significant_term_aggs.ts | 2 +-
.../lib/errors/distribution/get_buckets.ts | 2 +-
.../apm/server/lib/errors/get_error_groups.ts | 2 +-
.../add_filter_to_exclude_legacy_data.ts | 2 +-
.../create_apm_event_client/index.ts | 2 +-
.../unpack_processor_events.ts | 2 +-
.../create_internal_es_client/index.ts | 2 +-
.../lib/helpers/transaction_error_rate.ts | 2 +-
.../metrics/fetch_and_transform_metrics.ts | 2 +-
.../lib/metrics/transform_metrics_chart.ts | 2 +-
.../rum_client/ui_filters/get_es_filter.ts | 2 +-
.../lib/service_map/get_service_anomalies.ts | 2 +-
.../get_service_map_service_node_info.ts | 2 +-
.../lib/service_map/get_trace_sample_ids.ts | 2 +-
.../get_derived_service_annotations.ts | 2 +-
.../annotations/get_stored_annotations.ts | 2 +-
.../lib/services/annotations/index.test.ts | 2 +-
...et_service_instance_system_metric_stats.ts | 2 +-
.../services/get_service_metadata_details.ts | 2 +-
.../apm/server/lib/services/get_throughput.ts | 2 +-
.../get_service_profiling_statistics.ts | 2 +-
.../convert_settings_to_string.ts | 2 +-
.../find_exact_configuration.ts | 2 +-
.../search_configurations.ts | 2 +-
.../server/lib/transaction_groups/fetcher.ts | 2 +-
.../get_transaction_group_stats.ts | 2 +-
.../transactions/get_anomaly_data/fetcher.ts | 2 +-
.../transactions/get_latency_charts/index.ts | 2 +-
.../get_throughput_charts/index.ts | 2 +-
.../plugins/apm/server/projections/typings.ts | 2 +-
.../util/merge_projection/index.ts | 2 +-
x-pack/plugins/apm/server/utils/queries.ts | 2 +-
.../plugins/apm/server/utils/test_helpers.tsx | 2 +-
x-pack/plugins/apm/tsconfig.json | 2 +-
.../fleet/server/services/agents/crud.ts | 2 +-
.../fleet/server/services/agents/helpers.ts | 2 +-
.../services/api_keys/enrollment_api_key.ts | 2 +-
.../server/services/artifacts/artifacts.ts | 2 +-
.../server/services/artifacts/mappings.ts | 2 +-
.../fleet/server/services/artifacts/mocks.ts | 2 +-
x-pack/plugins/fleet/tsconfig.json | 2 +-
.../global_search_providers/tsconfig.json | 2 +-
x-pack/plugins/grokdebugger/tsconfig.json | 2 +-
.../index_lifecycle_management/tsconfig.json | 2 +-
x-pack/plugins/index_management/tsconfig.json | 2 +-
x-pack/plugins/infra/tsconfig.json | 2 +-
x-pack/plugins/ingest_pipelines/tsconfig.json | 2 +-
.../plugins/lens/server/routes/field_stats.ts | 2 +-
x-pack/plugins/lens/server/usage/task.ts | 2 +-
x-pack/plugins/lens/tsconfig.json | 2 +-
.../threshold/get_threshold_bucket_filters.ts | 2 +-
.../server/lib/machine_learning/index.test.ts | 2 +-
x-pack/plugins/snapshot_restore/tsconfig.json | 2 +-
.../common/build_sorted_events_query.ts | 4 +-
.../alert_types/es_query/action_context.ts | 2 +-
.../alert_types/es_query/alert_type.test.ts | 2 +-
.../server/alert_types/es_query/alert_type.ts | 4 +-
.../monitoring/workload_statistics.test.ts | 2 +-
.../server/monitoring/workload_statistics.ts | 2 +-
.../plugins/task_manager/server/task_store.ts | 2 +-
.../plugins/ui_actions_enhanced/tsconfig.json | 2 +-
x-pack/plugins/uptime/server/lib/lib.ts | 2 +-
.../lib/requests/get_monitor_availability.ts | 2 +-
.../lib/requests/get_monitor_details.ts | 2 +-
.../lib/requests/get_monitor_locations.ts | 2 +-
.../lib/requests/get_snapshot_counts.ts | 2 +-
.../lib/requests/search/query_context.ts | 2 +-
x-pack/plugins/watcher/tsconfig.json | 2 +-
.../trial/tests/annotations.ts | 2 +-
x-pack/test/tsconfig.json | 4 +-
x-pack/tsconfig.json | 117 ------------------
x-pack/typings/@elastic/eui/index.d.ts | 16 ---
x-pack/typings/cytoscape_dagre.d.ts | 8 --
x-pack/typings/index.d.ts | 33 -----
x-pack/typings/rison_node.d.ts | 30 -----
101 files changed, 188 insertions(+), 352 deletions(-)
delete mode 100644 test/typings/rison_node.d.ts
rename test/typings/index.d.ts => typings/cytoscape_dagre.d.ts (54%)
rename {x-pack/typings => typings}/elasticsearch/aggregations.d.ts (98%)
rename {x-pack/typings => typings}/elasticsearch/index.d.ts (95%)
rename {x-pack/typings => typings}/global_fetch.d.ts (66%)
rename {x-pack/typings => typings}/js_levenshtein.d.ts (59%)
rename {x-pack/typings => typings}/react_vis.d.ts (50%)
delete mode 100644 x-pack/tsconfig.json
delete mode 100644 x-pack/typings/@elastic/eui/index.d.ts
delete mode 100644 x-pack/typings/cytoscape_dagre.d.ts
delete mode 100644 x-pack/typings/index.d.ts
delete mode 100644 x-pack/typings/rison_node.d.ts
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index 8b306fc967115..050743114f657 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -14,7 +14,6 @@ import { Project } from './project';
export const PROJECTS = [
new Project(resolve(REPO_ROOT, 'tsconfig.json')),
new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), { name: 'kibana/test' }),
- new Project(resolve(REPO_ROOT, 'x-pack/tsconfig.json')),
new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), { name: 'x-pack/test' }),
new Project(resolve(REPO_ROOT, 'src/core/tsconfig.json')),
new Project(resolve(REPO_ROOT, 'x-pack/plugins/drilldowns/url_drilldown/tsconfig.json'), {
diff --git a/test/tsconfig.json b/test/tsconfig.json
index c3acf94f8c267..c68e15b2419a1 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -2,12 +2,11 @@
"extends": "../tsconfig.base.json",
"compilerOptions": {
"incremental": false,
- "types": ["node", "flot"]
+ "types": ["node"]
},
"include": [
"**/*",
- "../typings/elastic__node_crypto.d.ts",
- "typings/**/*",
+ "../typings/**/*",
"../packages/kbn-test/types/ftr_globals/**/*"
],
"exclude": ["plugin_functional/plugins/**/*", "interpreter_functional/plugins/**/*"],
diff --git a/test/typings/rison_node.d.ts b/test/typings/rison_node.d.ts
deleted file mode 100644
index dacb2524907be..0000000000000
--- a/test/typings/rison_node.d.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-declare module 'rison-node' {
- export type RisonValue = undefined | null | boolean | number | string | RisonObject | RisonArray;
-
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- export interface RisonArray extends Array {}
-
- export interface RisonObject {
- [key: string]: RisonValue;
- }
-
- export const decode: (input: string) => RisonValue;
-
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export const decode_object: (input: string) => RisonObject;
-
- export const encode: (input: Input) => string;
-
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export const encode_object: (input: Input) => string;
-}
diff --git a/tsconfig.json b/tsconfig.json
index f6ce6b92b7e02..18647153acb0a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,11 +3,25 @@
"compilerOptions": {
"incremental": false
},
- "include": ["kibana.d.ts", "src/**/*", "typings/**/*"],
+ "include": [
+ "kibana.d.ts",
+ "typings/**/*",
+
+ "src/cli/**/*",
+ "src/dev/**/*",
+ "src/fixtures/**/*",
+ "src/legacy/**/*",
+ "src/optimize/**/*",
+
+ "x-pack/mocks.ts",
+ "x-pack/typings/**/*",
+ "x-pack/tasks/**/*",
+ "x-pack/plugins/cases/**/*",
+ "x-pack/plugins/lists/**/*",
+ "x-pack/plugins/security_solution/**/*",
+ ],
"exclude": [
- "src/**/__fixtures__/**/*",
- "src/core/**/*",
- "src/plugins/**/*"
+ "x-pack/plugins/security_solution/cypress/**/*"
],
"references": [
{ "path": "./src/core/tsconfig.json" },
@@ -64,5 +78,64 @@
{ "path": "./src/plugins/visualize/tsconfig.json" },
{ "path": "./src/plugins/index_pattern_management/tsconfig.json" },
{ "path": "./src/plugins/index_pattern_field_editor/tsconfig.json" },
+
+ { "path": "./x-pack/plugins/actions/tsconfig.json" },
+ { "path": "./x-pack/plugins/alerting/tsconfig.json" },
+ { "path": "./x-pack/plugins/apm/tsconfig.json" },
+ { "path": "./x-pack/plugins/beats_management/tsconfig.json" },
+ { "path": "./x-pack/plugins/canvas/tsconfig.json" },
+ { "path": "./x-pack/plugins/cloud/tsconfig.json" },
+ { "path": "./x-pack/plugins/console_extensions/tsconfig.json" },
+ { "path": "./x-pack/plugins/data_enhanced/tsconfig.json" },
+ { "path": "./x-pack/plugins/dashboard_mode/tsconfig.json" },
+ { "path": "./x-pack/plugins/discover_enhanced/tsconfig.json" },
+ { "path": "./x-pack/plugins/drilldowns/url_drilldown/tsconfig.json" },
+ { "path": "./x-pack/plugins/embeddable_enhanced/tsconfig.json" },
+ { "path": "./x-pack/plugins/encrypted_saved_objects/tsconfig.json" },
+ { "path": "./x-pack/plugins/enterprise_search/tsconfig.json" },
+ { "path": "./x-pack/plugins/event_log/tsconfig.json" },
+ { "path": "./x-pack/plugins/features/tsconfig.json" },
+ { "path": "./x-pack/plugins/file_upload/tsconfig.json" },
+ { "path": "./x-pack/plugins/fleet/tsconfig.json" },
+ { "path": "./x-pack/plugins/global_search_bar/tsconfig.json" },
+ { "path": "./x-pack/plugins/global_search_providers/tsconfig.json" },
+ { "path": "./x-pack/plugins/global_search/tsconfig.json" },
+ { "path": "./x-pack/plugins/graph/tsconfig.json" },
+ { "path": "./x-pack/plugins/grokdebugger/tsconfig.json" },
+ { "path": "./x-pack/plugins/infra/tsconfig.json" },
+ { "path": "./x-pack/plugins/ingest_pipelines/tsconfig.json" },
+ { "path": "./x-pack/plugins/lens/tsconfig.json" },
+ { "path": "./x-pack/plugins/license_management/tsconfig.json" },
+ { "path": "./x-pack/plugins/licensing/tsconfig.json" },
+ { "path": "./x-pack/plugins/logstash/tsconfig.json" },
+ { "path": "./x-pack/plugins/maps_legacy_licensing/tsconfig.json" },
+ { "path": "./x-pack/plugins/maps/tsconfig.json" },
+ { "path": "./x-pack/plugins/ml/tsconfig.json" },
+ { "path": "./x-pack/plugins/monitoring/tsconfig.json" },
+ { "path": "./x-pack/plugins/observability/tsconfig.json" },
+ { "path": "./x-pack/plugins/osquery/tsconfig.json" },
+ { "path": "./x-pack/plugins/painless_lab/tsconfig.json" },
+ { "path": "./x-pack/plugins/saved_objects_tagging/tsconfig.json" },
+ { "path": "./x-pack/plugins/searchprofiler/tsconfig.json" },
+ { "path": "./x-pack/plugins/security/tsconfig.json" },
+ { "path": "./x-pack/plugins/snapshot_restore/tsconfig.json" },
+ { "path": "./x-pack/plugins/spaces/tsconfig.json" },
+ { "path": "./x-pack/plugins/stack_alerts/tsconfig.json" },
+ { "path": "./x-pack/plugins/task_manager/tsconfig.json" },
+ { "path": "./x-pack/plugins/telemetry_collection_xpack/tsconfig.json" },
+ { "path": "./x-pack/plugins/transform/tsconfig.json" },
+ { "path": "./x-pack/plugins/translations/tsconfig.json" },
+ { "path": "./x-pack/plugins/triggers_actions_ui/tsconfig.json" },
+ { "path": "./x-pack/plugins/ui_actions_enhanced/tsconfig.json" },
+ { "path": "./x-pack/plugins/upgrade_assistant/tsconfig.json" },
+ { "path": "./x-pack/plugins/runtime_fields/tsconfig.json" },
+ { "path": "./x-pack/plugins/index_management/tsconfig.json" },
+ { "path": "./x-pack/plugins/watcher/tsconfig.json" },
+ { "path": "./x-pack/plugins/rollup/tsconfig.json" },
+ { "path": "./x-pack/plugins/remote_clusters/tsconfig.json" },
+ { "path": "./x-pack/plugins/cross_cluster_replication/tsconfig.json"},
+ { "path": "./x-pack/plugins/index_lifecycle_management/tsconfig.json"},
+ { "path": "./x-pack/plugins/uptime/tsconfig.json" },
+ { "path": "./x-pack/plugins/xpack_legacy/tsconfig.json" }
]
}
diff --git a/test/typings/index.d.ts b/typings/cytoscape_dagre.d.ts
similarity index 54%
rename from test/typings/index.d.ts
rename to typings/cytoscape_dagre.d.ts
index 8ea94a280996e..0cf7cf8be2fee 100644
--- a/test/typings/index.d.ts
+++ b/typings/cytoscape_dagre.d.ts
@@ -6,14 +6,4 @@
* Side Public License, v 1.
*/
-declare module '*.html' {
- const template: string;
- // eslint-disable-next-line import/no-default-export
- export default template;
-}
-
-type MethodKeysOf = {
- [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
-}[keyof T];
-
-type PublicMethodsOf = Pick>;
+declare module 'cytoscape-dagre';
diff --git a/x-pack/typings/elasticsearch/aggregations.d.ts b/typings/elasticsearch/aggregations.d.ts
similarity index 98%
rename from x-pack/typings/elasticsearch/aggregations.d.ts
rename to typings/elasticsearch/aggregations.d.ts
index 077399c596d54..2b501c94889f4 100644
--- a/x-pack/typings/elasticsearch/aggregations.d.ts
+++ b/typings/elasticsearch/aggregations.d.ts
@@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 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 { Unionize, UnionToIntersection } from 'utility-types';
diff --git a/x-pack/typings/elasticsearch/index.d.ts b/typings/elasticsearch/index.d.ts
similarity index 95%
rename from x-pack/typings/elasticsearch/index.d.ts
rename to typings/elasticsearch/index.d.ts
index 41630e81f13e4..a84d4148f6fe7 100644
--- a/x-pack/typings/elasticsearch/index.d.ts
+++ b/typings/elasticsearch/index.d.ts
@@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 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 { ValuesType } from 'utility-types';
diff --git a/x-pack/typings/global_fetch.d.ts b/typings/global_fetch.d.ts
similarity index 66%
rename from x-pack/typings/global_fetch.d.ts
rename to typings/global_fetch.d.ts
index a79a76aebe539..597bc7e89497c 100644
--- a/x-pack/typings/global_fetch.d.ts
+++ b/typings/global_fetch.d.ts
@@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
*/
// This type needs to still exist due to apollo-link-http-common hasn't yet updated
diff --git a/typings/index.d.ts b/typings/index.d.ts
index 7192e70559743..c7186a0e5795b 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -24,6 +24,8 @@ declare module '*.svg' {
export default content;
}
+declare module 'axios/lib/adapters/xhr';
+
// Storybook references this module. It's @ts-ignored in the codebase but when
// built into its dist it strips that out. Add it here to avoid a type checking
// error.
diff --git a/x-pack/typings/js_levenshtein.d.ts b/typings/js_levenshtein.d.ts
similarity index 59%
rename from x-pack/typings/js_levenshtein.d.ts
rename to typings/js_levenshtein.d.ts
index f693e17244db1..7c934333dbc7b 100644
--- a/x-pack/typings/js_levenshtein.d.ts
+++ b/typings/js_levenshtein.d.ts
@@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
*/
declare module 'js-levenshtein' {
diff --git a/x-pack/typings/react_vis.d.ts b/typings/react_vis.d.ts
similarity index 50%
rename from x-pack/typings/react_vis.d.ts
rename to typings/react_vis.d.ts
index bcfbafd47fbc7..209dd398e86f4 100644
--- a/x-pack/typings/react_vis.d.ts
+++ b/typings/react_vis.d.ts
@@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
*/
declare module 'react-vis';
diff --git a/x-pack/examples/alerting_example/tsconfig.json b/x-pack/examples/alerting_example/tsconfig.json
index 99e0f1f0e7c9e..95d42d40aceb3 100644
--- a/x-pack/examples/alerting_example/tsconfig.json
+++ b/x-pack/examples/alerting_example/tsconfig.json
@@ -9,7 +9,7 @@
"public/**/*.tsx",
"server/**/*.ts",
"common/**/*.ts",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"exclude": [],
"references": [
diff --git a/x-pack/examples/embedded_lens_example/tsconfig.json b/x-pack/examples/embedded_lens_example/tsconfig.json
index 2bf577e87041c..195db6effc5e6 100644
--- a/x-pack/examples/embedded_lens_example/tsconfig.json
+++ b/x-pack/examples/embedded_lens_example/tsconfig.json
@@ -9,7 +9,7 @@
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"exclude": [],
"references": [
diff --git a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
index 05e5f39d4d628..567baca039d76 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
+++ b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
@@ -9,7 +9,7 @@
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"exclude": [],
"references": [
diff --git a/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts b/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
index 8c7947201927f..c86cf769d7529 100644
--- a/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
+++ b/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import {
ERROR_GROUP_ID,
PROCESSOR_EVENT,
diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx
index 80df113e18190..d0d09f703ae9f 100644
--- a/x-pack/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx
@@ -19,7 +19,7 @@ import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../typings/elasticsearch';
+} from '../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../observability/typings/common';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { APMConfig } from '../../server';
diff --git a/x-pack/plugins/apm/scripts/shared/get_es_client.ts b/x-pack/plugins/apm/scripts/shared/get_es_client.ts
index f17a55cf4e215..7a8e09423ff15 100644
--- a/x-pack/plugins/apm/scripts/shared/get_es_client.ts
+++ b/x-pack/plugins/apm/scripts/shared/get_es_client.ts
@@ -10,7 +10,7 @@ import { ApiKeyAuth, BasicAuth } from '@elastic/elasticsearch/lib/pool';
import {
ESSearchResponse,
ESSearchRequest,
-} from '../../../../typings/elasticsearch';
+} from '../../../../../typings/elasticsearch';
export type ESClient = ReturnType;
diff --git a/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts b/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts
index 727b0c1f04cf4..d5706ac9063ed 100644
--- a/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts
@@ -9,7 +9,7 @@ import { ThresholdMetActionGroupId } from '../../../common/alert_types';
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../../typings/elasticsearch';
+} from '../../../../../../typings/elasticsearch';
import {
AlertInstanceContext,
AlertInstanceState,
diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
index 86456114698cb..bea90109725d0 100644
--- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { MetricsAggregationResponsePart } from '../../../../../../typings/elasticsearch/aggregations';
+import { MetricsAggregationResponsePart } from '../../../../../../../typings/elasticsearch/aggregations';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts
index 50ed02879a4d2..98063e3e1e3fd 100644
--- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts
+++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts
@@ -11,7 +11,7 @@ import { RequestParams } from '@elastic/elasticsearch';
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../../../typings/elasticsearch';
+} from '../../../../../../../typings/elasticsearch';
import { ApmIndicesConfig } from '../../settings/apm_indices/get_apm_indices';
import { tasks } from './tasks';
import { APMDataTelemetry } from '../types';
diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts
index 36c15366b9b48..e9744c6614641 100644
--- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts
+++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts
@@ -7,7 +7,7 @@
import { ValuesType } from 'utility-types';
import { flatten, merge, sortBy, sum, pickBy } from 'lodash';
-import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
+import { AggregationOptionsByType } from '../../../../../../../typings/elasticsearch/aggregations';
import { ProcessorEvent } from '../../../../common/processor_event';
import { TelemetryTask } from '.';
import { AGENT_NAMES, RUM_AGENT_NAMES } from '../../../../common/agent_name';
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
index e2411d1d17adc..f613a0dbca402 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
@@ -11,8 +11,8 @@ import {
processSignificantTermAggs,
TopSigTerm,
} from '../process_significant_term_aggs';
-import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../../typings/elasticsearch/aggregations';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import {
environmentQuery,
rangeQuery,
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_duration_for_percentile.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_duration_for_percentile.ts
index 27f69c3ca7d56..02141f5f9e76f 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_duration_for_percentile.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_duration_for_percentile.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
import { withApmSpan } from '../../../utils/with_apm_span';
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_latency_distribution.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_latency_distribution.ts
index eab09e814c18d..b800a21ffc341 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_latency_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_latency_distribution.ts
@@ -6,8 +6,8 @@
*/
import { isEmpty, dropRightWhile } from 'lodash';
-import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../../typings/elasticsearch/aggregations';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_max_latency.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_max_latency.ts
index 2777c0944afd1..5f12c86a9c70c 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_max_latency.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/get_max_latency.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
import { withApmSpan } from '../../../utils/with_apm_span';
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
index 824b290a6ba60..6afca46ec7391 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
-import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../../typings/elasticsearch/aggregations';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import {
environmentQuery,
rangeQuery,
diff --git a/x-pack/plugins/apm/server/lib/correlations/process_significant_term_aggs.ts b/x-pack/plugins/apm/server/lib/correlations/process_significant_term_aggs.ts
index 1fe50c869f5bf..2732cd45c342e 100644
--- a/x-pack/plugins/apm/server/lib/correlations/process_significant_term_aggs.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/process_significant_term_aggs.ts
@@ -9,7 +9,7 @@ import { orderBy } from 'lodash';
import {
AggregationOptionsByType,
AggregationResultOf,
-} from '../../../../../typings/elasticsearch/aggregations';
+} from '../../../../../../typings/elasticsearch/aggregations';
export interface TopSigTerm {
fieldName: string;
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
index 1e161b0383f0b..462c9bcdc4310 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import {
ERROR_GROUP_ID,
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index 5371d69caaa99..1c262ebf882b2 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { SortOptions } from '../../../../../typings/elasticsearch/aggregations';
+import { SortOptions } from '../../../../../../typings/elasticsearch/aggregations';
import {
ERROR_CULPRIT,
ERROR_EXC_HANDLED,
diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/add_filter_to_exclude_legacy_data.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/add_filter_to_exclude_legacy_data.ts
index 0f2bff09f99c1..96bc8897e62fd 100644
--- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/add_filter_to_exclude_legacy_data.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/add_filter_to_exclude_legacy_data.ts
@@ -10,7 +10,7 @@ import { OBSERVER_VERSION_MAJOR } from '../../../../../common/elasticsearch_fiel
import {
ESSearchRequest,
ESFilter,
-} from '../../../../../../../typings/elasticsearch';
+} from '../../../../../../../../typings/elasticsearch';
/*
Adds a range query to the ES request to exclude legacy data
diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts
index 368c0eb305f21..e04b3a70a7593 100644
--- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts
@@ -14,7 +14,7 @@ import {
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../../../../typings/elasticsearch';
+} from '../../../../../../../../typings/elasticsearch';
import { unwrapEsResponse } from '../../../../../../observability/server';
import { ProcessorEvent } from '../../../../../common/processor_event';
import { APMError } from '../../../../../typings/es_schemas/ui/apm_error';
diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts
index 38989d172a73f..76e615f42bb64 100644
--- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/unpack_processor_events.ts
@@ -11,7 +11,7 @@ import { ProcessorEvent } from '../../../../../common/processor_event';
import {
ESSearchRequest,
ESFilter,
-} from '../../../../../../../typings/elasticsearch';
+} from '../../../../../../../../typings/elasticsearch';
import { APMEventESSearchRequest } from '.';
import {
ApmIndicesConfig,
diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts
index ff1509dc83d15..4faf80d7ca8db 100644
--- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts
@@ -13,7 +13,7 @@ import { APMRequestHandlerContext } from '../../../../routes/typings';
import {
ESSearchResponse,
ESSearchRequest,
-} from '../../../../../../../typings/elasticsearch';
+} from '../../../../../../../../typings/elasticsearch';
import {
callAsyncWithDebug,
getDebugBody,
diff --git a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts
index e22c2514a89a4..9bec5eb4a247c 100644
--- a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts
@@ -10,7 +10,7 @@ import { EventOutcome } from '../../../common/event_outcome';
import {
AggregationOptionsByType,
AggregationResultOf,
-} from '../../../../../typings/elasticsearch/aggregations';
+} from '../../../../../../typings/elasticsearch/aggregations';
export const getOutcomeAggregation = () => ({
terms: {
diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index ef24b531d8046..30234447821ec 100644
--- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -6,7 +6,7 @@
*/
import { Overwrite, Unionize } from 'utility-types';
-import { AggregationOptionsByType } from '../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch';
import { getMetricsProjection } from '../../projections/metrics';
import { mergeProjection } from '../../projections/util/merge_projection';
import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client';
diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
index a7c5fc6628c52..17759f9094a87 100644
--- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
@@ -6,7 +6,7 @@
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
-import { ESSearchResponse } from '../../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { getVizColorForIndex } from '../../../common/viz_colors';
import { GenericMetricsRequest } from './fetch_and_transform_metrics';
import { ChartBase } from './types';
diff --git a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts
index aed361f13bd7d..43cbb485c4510 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { UIFilters } from '../../../../typings/ui_filters';
import { localUIFilters, localUIFilterNames } from './local_ui_filters/config';
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
index f08cc27b2e59c..8c97a3993e8c0 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
@@ -7,7 +7,7 @@
import Boom from '@hapi/boom';
import { sortBy, uniqBy } from 'lodash';
-import { ESSearchResponse } from '../../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { MlPluginSetup } from '../../../../ml/server';
import { PromiseReturnType } from '../../../../observability/typings/common';
import { getSeverity, ML_ERRORS } from '../../../common/anomaly_detection';
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index 7da3ce772ef5f..c1dfed377a763 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../typings/elasticsearch';
import {
METRIC_CGROUP_MEMORY_USAGE_BYTES,
METRIC_SYSTEM_CPU_PERCENT,
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
index 2b949863bcb30..8bc1b1f0562f5 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
@@ -7,7 +7,7 @@
import Boom from '@hapi/boom';
import { sortBy, take, uniq } from 'lodash';
-import { ESFilter } from '../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../typings/elasticsearch';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
index efe9608edb95d..028c8c042c8dc 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
@@ -6,7 +6,7 @@
*/
import { isFiniteNumber } from '../../../../common/utils/is_finite_number';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { Annotation, AnnotationType } from '../../../../common/annotations';
import {
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
index 87ee0e9830fce..e0329e5f60e19 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
@@ -12,7 +12,7 @@ import {
unwrapEsResponse,
WrappedElasticsearchClientError,
} from '../../../../../observability/server';
-import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../../../typings/elasticsearch';
import { Annotation as ESAnnotation } from '../../../../../observability/common/annotations';
import { ScopedAnnotationsClient } from '../../../../../observability/server';
import { Annotation, AnnotationType } from '../../../../common/annotations';
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
index d86016ed9d505..e2597a4a79cba 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
@@ -8,7 +8,7 @@
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../../../typings/elasticsearch';
+} from '../../../../../../../typings/elasticsearch';
import {
inspectSearchParams,
SearchParamsMock,
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
index 3e788ca8ddf83..6a72f817b3f69 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../../typings/elasticsearch';
import {
environmentQuery,
rangeQuery,
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
index a064d5b3008c2..a71772d1429cb 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
@@ -6,7 +6,7 @@
*/
import { ProcessorEvent } from '../../../common/processor_event';
-import { SortOptions } from '../../../../../typings/elasticsearch';
+import { SortOptions } from '../../../../../../typings/elasticsearch';
import {
AGENT,
CLOUD,
diff --git a/x-pack/plugins/apm/server/lib/services/get_throughput.ts b/x-pack/plugins/apm/server/lib/services/get_throughput.ts
index 490eec337840e..5f5008a28c232 100644
--- a/x-pack/plugins/apm/server/lib/services/get_throughput.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_throughput.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../typings/elasticsearch';
import {
SERVICE_NAME,
TRANSACTION_TYPE,
diff --git a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts
index 8b60d39a8de5d..858f36e6e2c13 100644
--- a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts
+++ b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts
@@ -15,7 +15,7 @@ import {
getValueTypeConfig,
} from '../../../../common/profiling';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import {
PROFILE_STACK,
PROFILE_TOP_ID,
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/convert_settings_to_string.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/convert_settings_to_string.ts
index b38ca71d93c0a..2d6eff33b5b4e 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/convert_settings_to_string.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/convert_settings_to_string.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESSearchHit } from '../../../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../../../typings/elasticsearch';
import { AgentConfiguration } from '../../../../common/agent_configuration/configuration_types';
// needed for backwards compatability
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
index 55d00b70b8c29..972c076d88e76 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESSearchHit } from '../../../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../../../typings/elasticsearch';
import { AgentConfiguration } from '../../../../common/agent_configuration/configuration_types';
import {
SERVICE_ENVIRONMENT,
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
index 0e7205c309e9f..12ba0939508e3 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESSearchHit } from '../../../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../../../typings/elasticsearch';
import {
SERVICE_NAME,
SERVICE_ENVIRONMENT,
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index ce0b6cf2a64fe..6308236000a53 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -8,7 +8,7 @@
import { sortBy, take } from 'lodash';
import moment from 'moment';
import { Unionize } from 'utility-types';
-import { AggregationOptionsByType } from '../../../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../observability/typings/common';
import {
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
index 5ee46bf1a5918..5409f919bf895 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
@@ -8,7 +8,7 @@
import { merge } from 'lodash';
import { TRANSACTION_TYPE } from '../../../common/elasticsearch_fieldnames';
import { arrayUnionToCallable } from '../../../common/utils/array_union_to_callable';
-import { AggregationInputMap } from '../../../../../typings/elasticsearch';
+import { AggregationInputMap } from '../../../../../../typings/elasticsearch';
import { TransactionGroupRequestBase, TransactionGroupSetup } from './fetcher';
import { getTransactionDurationFieldForAggregatedTransactions } from '../helpers/aggregated_transactions';
import { withApmSpan } from '../../utils/with_apm_span';
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
index cfd09f0207536..a35780539a256 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import { rangeQuery } from '../../../../server/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
index 31b5c6ff64dfd..5bd80f500fd2b 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import {
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
index 3b7ffafff0d2a..a0225eb47e584 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import {
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/server/projections/typings.ts b/x-pack/plugins/apm/server/projections/typings.ts
index 725756b61b209..558f165d43cf5 100644
--- a/x-pack/plugins/apm/server/projections/typings.ts
+++ b/x-pack/plugins/apm/server/projections/typings.ts
@@ -9,7 +9,7 @@ import {
AggregationOptionsByType,
AggregationInputMap,
ESSearchBody,
-} from '../../../../typings/elasticsearch';
+} from '../../../../../typings/elasticsearch';
import { APMEventESSearchRequest } from '../lib/helpers/create_es_client/create_apm_event_client';
export type Projection = Omit & {
diff --git a/x-pack/plugins/apm/server/projections/util/merge_projection/index.ts b/x-pack/plugins/apm/server/projections/util/merge_projection/index.ts
index 33d8b127137f0..7f08707064862 100644
--- a/x-pack/plugins/apm/server/projections/util/merge_projection/index.ts
+++ b/x-pack/plugins/apm/server/projections/util/merge_projection/index.ts
@@ -10,7 +10,7 @@ import { DeepPartial } from 'utility-types';
import {
AggregationInputMap,
ESSearchBody,
-} from '../../../../../../typings/elasticsearch';
+} from '../../../../../../../typings/elasticsearch';
import { APMEventESSearchRequest } from '../../../lib/helpers/create_es_client/create_apm_event_client';
import { Projection } from '../../typings';
diff --git a/x-pack/plugins/apm/server/utils/queries.ts b/x-pack/plugins/apm/server/utils/queries.ts
index 6eab50d089821..3cbcb0a5b684f 100644
--- a/x-pack/plugins/apm/server/utils/queries.ts
+++ b/x-pack/plugins/apm/server/utils/queries.ts
@@ -6,7 +6,7 @@
*/
import { esKuery } from '../../../../../src/plugins/data/server';
-import { ESFilter } from '../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../typings/elasticsearch';
import { SERVICE_ENVIRONMENT } from '../../common/elasticsearch_fieldnames';
import {
ENVIRONMENT_ALL,
diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx
index e804183c78867..6252c33c5994d 100644
--- a/x-pack/plugins/apm/server/utils/test_helpers.tsx
+++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx
@@ -10,7 +10,7 @@ import { PromiseReturnType } from '../../../observability/typings/common';
import {
ESSearchRequest,
ESSearchResponse,
-} from '../../../../typings/elasticsearch';
+} from '../../../../../typings/elasticsearch';
import { UIFilters } from '../../typings/ui_filters';
interface Options {
diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json
index ea1602d916822..ffbf11c23f63a 100644
--- a/x-pack/plugins/apm/tsconfig.json
+++ b/x-pack/plugins/apm/tsconfig.json
@@ -8,7 +8,7 @@
"declarationMap": true
},
"include": [
- "../../typings/**/*",
+ "../../../typings/**/*",
"common/**/*",
"public/**/*",
"scripts/**/*",
diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts
index 975ca7fec08b1..06353d831c60f 100644
--- a/x-pack/plugins/fleet/server/services/agents/crud.ts
+++ b/x-pack/plugins/fleet/server/services/agents/crud.ts
@@ -14,7 +14,7 @@ import { appContextService, agentPolicyService } from '../../services';
import type { FleetServerAgent } from '../../../common';
import { isAgentUpgradeable, SO_SEARCH_LIMIT } from '../../../common';
import { AGENT_SAVED_OBJECT_TYPE, AGENTS_INDEX } from '../../constants';
-import type { ESSearchHit } from '../../../../../typings/elasticsearch';
+import type { ESSearchHit } from '../../../../../../typings/elasticsearch';
import { escapeSearchQueryPhrase, normalizeKuery } from '../saved_object';
import type { KueryNode } from '../../../../../../src/plugins/data/server';
import { esKuery } from '../../../../../../src/plugins/data/server';
diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts
index 1dab3b64755fd..3fdb347ed246b 100644
--- a/x-pack/plugins/fleet/server/services/agents/helpers.ts
+++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import type { ESSearchHit } from '../../../../../typings/elasticsearch';
+import type { ESSearchHit } from '../../../../../../typings/elasticsearch';
import type { Agent, AgentSOAttributes, FleetServerAgent } from '../../types';
export function searchHitToAgent(hit: ESSearchHit): Agent {
diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts
index 387d69c5c0ca7..4365c3913f433 100644
--- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts
+++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts
@@ -12,7 +12,7 @@ import type { GetResponse } from 'elasticsearch';
import { ResponseError } from '@elastic/elasticsearch/lib/errors';
import type { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server';
-import type { ESSearchResponse as SearchResponse } from '../../../../../typings/elasticsearch';
+import type { ESSearchResponse as SearchResponse } from '../../../../../../typings/elasticsearch';
import type { EnrollmentAPIKey, FleetServerEnrollmentAPIKey } from '../../types';
import { ENROLLMENT_API_KEYS_INDEX } from '../../constants';
import { agentPolicyService } from '../agent_policy';
diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts
index 02a0d5669d967..2a5f39c4e8a26 100644
--- a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts
+++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts
@@ -16,7 +16,7 @@ import type { ElasticsearchClient } from 'kibana/server';
import type { ListResult } from '../../../common';
import { FLEET_SERVER_ARTIFACTS_INDEX } from '../../../common';
-import type { ESSearchHit, ESSearchResponse } from '../../../../../typings/elasticsearch';
+import type { ESSearchHit, ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { ArtifactsElasticsearchError } from '../../errors';
diff --git a/x-pack/plugins/fleet/server/services/artifacts/mappings.ts b/x-pack/plugins/fleet/server/services/artifacts/mappings.ts
index bdc50d444c862..863eff5aac74c 100644
--- a/x-pack/plugins/fleet/server/services/artifacts/mappings.ts
+++ b/x-pack/plugins/fleet/server/services/artifacts/mappings.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import type { ESSearchHit } from '../../../../../typings/elasticsearch';
+import type { ESSearchHit } from '../../../../../../typings/elasticsearch';
import type { Artifact, ArtifactElasticsearchProperties } from './types';
import { ARTIFACT_DOWNLOAD_RELATIVE_PATH } from './constants';
diff --git a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts
index a89d6913c680c..b1e01208a24ca 100644
--- a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts
+++ b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts
@@ -10,7 +10,7 @@ import type { ApiResponse } from '@elastic/elasticsearch';
import { ResponseError } from '@elastic/elasticsearch/lib/errors';
import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks';
-import type { ESSearchHit, ESSearchResponse } from '../../../../../typings/elasticsearch';
+import type { ESSearchHit, ESSearchResponse } from '../../../../../../typings/elasticsearch';
import type { Artifact, ArtifactElasticsearchProperties, ArtifactsClientInterface } from './types';
diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json
index e6dc206912c4b..a20d82de3c859 100644
--- a/x-pack/plugins/fleet/tsconfig.json
+++ b/x-pack/plugins/fleet/tsconfig.json
@@ -15,7 +15,7 @@
"server/**/*.json",
"scripts/**/*",
"package.json",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/global_search_providers/tsconfig.json b/x-pack/plugins/global_search_providers/tsconfig.json
index 381d314b2e530..f2759954a6845 100644
--- a/x-pack/plugins/global_search_providers/tsconfig.json
+++ b/x-pack/plugins/global_search_providers/tsconfig.json
@@ -10,7 +10,7 @@
"include": [
"public/**/*",
"server/**/*",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/grokdebugger/tsconfig.json b/x-pack/plugins/grokdebugger/tsconfig.json
index 34cf8d74c0024..51d2d0b6db0ea 100644
--- a/x-pack/plugins/grokdebugger/tsconfig.json
+++ b/x-pack/plugins/grokdebugger/tsconfig.json
@@ -11,7 +11,7 @@
"common/**/*",
"public/**/*",
"server/**/*",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/index_lifecycle_management/tsconfig.json b/x-pack/plugins/index_lifecycle_management/tsconfig.json
index 73dcc62132cbf..75bd775a36749 100644
--- a/x-pack/plugins/index_lifecycle_management/tsconfig.json
+++ b/x-pack/plugins/index_lifecycle_management/tsconfig.json
@@ -12,7 +12,7 @@
"common/**/*",
"public/**/*",
"server/**/*",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/index_management/tsconfig.json b/x-pack/plugins/index_management/tsconfig.json
index 87be6cfc2d627..81a96a77cef83 100644
--- a/x-pack/plugins/index_management/tsconfig.json
+++ b/x-pack/plugins/index_management/tsconfig.json
@@ -13,7 +13,7 @@
"public/**/*",
"server/**/*",
"test/**/*",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json
index 026da311192d2..765af7974a2f1 100644
--- a/x-pack/plugins/infra/tsconfig.json
+++ b/x-pack/plugins/infra/tsconfig.json
@@ -8,7 +8,7 @@
"declarationMap": true
},
"include": [
- "../../typings/**/*",
+ "../../../typings/**/*",
"common/**/*",
"public/**/*",
"scripts/**/*",
diff --git a/x-pack/plugins/ingest_pipelines/tsconfig.json b/x-pack/plugins/ingest_pipelines/tsconfig.json
index 5d78992600e81..a248bc9f337fe 100644
--- a/x-pack/plugins/ingest_pipelines/tsconfig.json
+++ b/x-pack/plugins/ingest_pipelines/tsconfig.json
@@ -12,7 +12,7 @@
"public/**/*",
"server/**/*",
"__jest__/**/*",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts
index 3b293f9af7f28..49ea8c2076f7a 100644
--- a/x-pack/plugins/lens/server/routes/field_stats.ts
+++ b/x-pack/plugins/lens/server/routes/field_stats.ts
@@ -11,7 +11,7 @@ import { schema } from '@kbn/config-schema';
import { CoreSetup } from 'src/core/server';
import { IFieldType } from 'src/plugins/data/common';
import { SavedObjectNotFound } from '../../../../../src/plugins/kibana_utils/common';
-import { ESSearchResponse } from '../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
import { FieldStatsResponse, BASE_API_URL } from '../../common';
import { PluginStartContract } from '../plugin';
diff --git a/x-pack/plugins/lens/server/usage/task.ts b/x-pack/plugins/lens/server/usage/task.ts
index f9296e0a41ca3..d583e1628cbe8 100644
--- a/x-pack/plugins/lens/server/usage/task.ts
+++ b/x-pack/plugins/lens/server/usage/task.ts
@@ -16,7 +16,7 @@ import {
} from '../../../task_manager/server';
import { getVisualizationCounts } from './visualization_counts';
-import { ESSearchResponse } from '../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
// This task is responsible for running daily and aggregating all the Lens click event objects
// into daily rolled-up documents, which will be used in reporting click stats
diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json
index 636d2f44b0217..dfddccbf20392 100644
--- a/x-pack/plugins/lens/tsconfig.json
+++ b/x-pack/plugins/lens/tsconfig.json
@@ -13,7 +13,7 @@
"common/**/*",
"public/**/*",
"server/**/*",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts
index 208727944765c..73068a73a38a3 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts
@@ -6,7 +6,7 @@
*/
import { Filter } from 'src/plugins/data/common';
-import { ESFilter } from '../../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../../typings/elasticsearch';
import { ThresholdSignalHistory, ThresholdSignalHistoryRecord } from '../types';
export const getThresholdBucketFilters = async ({
diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts
index 1f49ac7bf5019..30dd5adf6123b 100644
--- a/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../typings/elasticsearch';
import { getExceptionListItemSchemaMock } from '../../../../lists/common/schemas/response/exception_list_item_schema.mock';
import { getAnomalies, AnomaliesSearchParams } from '.';
diff --git a/x-pack/plugins/snapshot_restore/tsconfig.json b/x-pack/plugins/snapshot_restore/tsconfig.json
index 5d962c7c17aff..39beda02977e1 100644
--- a/x-pack/plugins/snapshot_restore/tsconfig.json
+++ b/x-pack/plugins/snapshot_restore/tsconfig.json
@@ -13,7 +13,7 @@
"public/**/*",
"server/**/*",
"test/**/*",
- "../../typings/**/*",
+ "../../../typings/**/*",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts
index c7ed7df38be0f..add3e1f59e20e 100644
--- a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts
+++ b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
-import { ESSearchBody, ESSearchRequest } from '../../../typings/elasticsearch';
-import { SortOrder } from '../../../typings/elasticsearch/aggregations';
+import { ESSearchBody, ESSearchRequest } from '../../../../typings/elasticsearch';
+import { SortOrder } from '../../../../typings/elasticsearch/aggregations';
type BuildSortedEventsQueryOpts = Pick &
Pick, 'index' | 'size'>;
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/action_context.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/action_context.ts
index 617da80bcc8d7..f0596a9fcb964 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/action_context.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/action_context.ts
@@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import { AlertExecutorOptions, AlertInstanceContext } from '../../../../alerting/server';
import { EsQueryAlertParams } from './alert_type_params';
-import { ESSearchHit } from '../../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../../typings/elasticsearch';
// alert type context provided to actions
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts
index 86e07fa64af66..4adc7c05821f9 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts
@@ -17,7 +17,7 @@ import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { getAlertType, ConditionMetAlertInstanceId, ActionGroupId } from './alert_type';
import { EsQueryAlertParams, EsQueryAlertState } from './alert_type_params';
import { ActionContext } from './action_context';
-import { ESSearchResponse, ESSearchRequest } from '../../../../../typings/elasticsearch';
+import { ESSearchResponse, ESSearchRequest } from '../../../../../../typings/elasticsearch';
describe('alertType', () => {
const logger = loggingSystemMock.create().get();
diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts
index 74af8b0038a3a..7734c59425a16 100644
--- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts
+++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts
@@ -7,7 +7,7 @@
import { i18n } from '@kbn/i18n';
import { Logger } from 'src/core/server';
-import { ESSearchResponse } from '../../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { AlertType, AlertExecutorOptions } from '../../types';
import { ActionContext, EsQueryAlertActionContext, addMessages } from './action_context';
import {
@@ -19,7 +19,7 @@ import { STACK_ALERTS_FEATURE_ID } from '../../../common';
import { ComparatorFns, getHumanReadableComparator } from '../lib';
import { parseDuration } from '../../../../alerting/server';
import { buildSortedEventsQuery } from '../../../common/build_sorted_events_query';
-import { ESSearchHit } from '../../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../../typings/elasticsearch';
export const ES_QUERY_ID = '.es-query';
diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
index a5a7954204492..46d8478a7ecfa 100644
--- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
+++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
@@ -14,7 +14,7 @@ import {
estimateRecurringTaskScheduling,
} from './workload_statistics';
import { ConcreteTaskInstance } from '../task';
-import { AggregationResultOf, ESSearchResponse } from '../../../../typings/elasticsearch';
+import { AggregationResultOf, ESSearchResponse } from '../../../../../typings/elasticsearch';
import { times } from 'lodash';
import { taskStoreMock } from '../task_store.mock';
import { of, Subject } from 'rxjs';
diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts
index 75331615e3f07..08850c8650519 100644
--- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts
+++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts
@@ -12,7 +12,7 @@ import { JsonObject } from 'src/plugins/kibana_utils/common';
import { keyBy, mapValues } from 'lodash';
import { AggregatedStatProvider } from './runtime_statistics_aggregator';
import { parseIntervalAsSecond, asInterval, parseIntervalAsMillisecond } from '../lib/intervals';
-import { AggregationResultOf } from '../../../../typings/elasticsearch';
+import { AggregationResultOf } from '../../../../../typings/elasticsearch';
import { HealthStatus } from './monitoring_stats_stream';
import { TaskStore } from '../task_store';
diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts
index 0b54f2779065f..083ce1507e6e5 100644
--- a/x-pack/plugins/task_manager/server/task_store.ts
+++ b/x-pack/plugins/task_manager/server/task_store.ts
@@ -31,7 +31,7 @@ import {
} from './task';
import { TaskTypeDictionary } from './task_type_dictionary';
-import { ESSearchResponse, ESSearchBody } from '../../../typings/elasticsearch';
+import { ESSearchResponse, ESSearchBody } from '../../../../typings/elasticsearch';
export interface StoreOpts {
esClient: ElasticsearchClient;
diff --git a/x-pack/plugins/ui_actions_enhanced/tsconfig.json b/x-pack/plugins/ui_actions_enhanced/tsconfig.json
index af24c30389b8b..39318770126e5 100644
--- a/x-pack/plugins/ui_actions_enhanced/tsconfig.json
+++ b/x-pack/plugins/ui_actions_enhanced/tsconfig.json
@@ -11,7 +11,7 @@
"public/**/*",
"server/**/*",
"common/**/*",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/plugins/uptime/server/lib/lib.ts b/x-pack/plugins/uptime/server/lib/lib.ts
index 5ac56d14c171d..1a7cef504b019 100644
--- a/x-pack/plugins/uptime/server/lib/lib.ts
+++ b/x-pack/plugins/uptime/server/lib/lib.ts
@@ -16,7 +16,7 @@ import { UMBackendFrameworkAdapter } from './adapters';
import { UMLicenseCheck } from './domains';
import { UptimeRequests } from './requests';
import { savedObjectsAdapter } from './saved_objects';
-import { ESSearchResponse } from '../../../../typings/elasticsearch';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
export interface UMDomainLibs {
requests: UptimeRequests;
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts
index 26b7c84b17c98..f6195db6f6bce 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts
@@ -8,7 +8,7 @@
import { UMElasticsearchQueryFn } from '../adapters';
import { GetMonitorAvailabilityParams, Ping } from '../../../common/runtime_types';
import { AfterKey } from './get_monitor_status';
-import { SortOptions } from '../../../../../typings/elasticsearch';
+import { SortOptions } from '../../../../../../typings/elasticsearch';
export interface AvailabilityKey {
monitorId: string;
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts
index 7034fef8a9e3b..e4b8fac3b44dd 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts
@@ -9,7 +9,7 @@ import { UMElasticsearchQueryFn } from '../adapters';
import { MonitorDetails, Ping } from '../../../common/runtime_types';
import { formatFilterString } from '../alerts/status_check';
import { UptimeESClient } from '../lib';
-import { ESSearchBody } from '../../../../../typings/elasticsearch';
+import { ESSearchBody } from '../../../../../../typings/elasticsearch';
export interface GetMonitorDetailsParams {
monitorId: string;
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts
index 64f62de6397ce..24e7376160b67 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts
@@ -8,7 +8,7 @@
import { UMElasticsearchQueryFn } from '../adapters';
import { MonitorLocations, MonitorLocation } from '../../../common/runtime_types';
import { UNNAMED_LOCATION } from '../../../common/constants';
-import { SortOptions } from '../../../../../typings/elasticsearch';
+import { SortOptions } from '../../../../../../typings/elasticsearch';
/**
* Fetch data for the monitor page title.
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
index 2999f9ebca065..0e47f2a3d56c2 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
@@ -9,7 +9,7 @@ import { UMElasticsearchQueryFn } from '../adapters';
import { CONTEXT_DEFAULTS } from '../../../common/constants';
import { Snapshot } from '../../../common/runtime_types';
import { QueryContext } from './search';
-import { ESFilter } from '../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../typings/elasticsearch';
export interface GetSnapshotCountParams {
dateRangeStart: string;
diff --git a/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts b/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts
index f377ba74dc8af..d749460ba997c 100644
--- a/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts
@@ -10,7 +10,7 @@ import { CursorPagination } from './types';
import { parseRelativeDate } from '../../helper';
import { CursorDirection, SortOrder } from '../../../../common/runtime_types';
import { UptimeESClient } from '../../lib';
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../../../../typings/elasticsearch';
export class QueryContext {
callES: UptimeESClient;
diff --git a/x-pack/plugins/watcher/tsconfig.json b/x-pack/plugins/watcher/tsconfig.json
index 4680847ba486d..e8dabe8cd40a9 100644
--- a/x-pack/plugins/watcher/tsconfig.json
+++ b/x-pack/plugins/watcher/tsconfig.json
@@ -13,7 +13,7 @@
"common/**/*",
"tests_client_integration/**/*",
"__fixtures__/*",
- "../../typings/**/*"
+ "../../../typings/**/*"
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/test/observability_api_integration/trial/tests/annotations.ts b/x-pack/test/observability_api_integration/trial/tests/annotations.ts
index ef4e34a2818de..928d160a5df9e 100644
--- a/x-pack/test/observability_api_integration/trial/tests/annotations.ts
+++ b/x-pack/test/observability_api_integration/trial/tests/annotations.ts
@@ -8,7 +8,7 @@
import expect from '@kbn/expect';
import { JsonObject } from 'src/plugins/kibana_utils/common';
import { Annotation } from '../../../../plugins/observability/common/annotations';
-import { ESSearchHit } from '../../../../typings/elasticsearch';
+import { ESSearchHit } from '../../../../../typings/elasticsearch';
import { FtrProviderContext } from '../../common/ftr_provider_context';
const DEFAULT_INDEX_NAME = 'observability-annotations';
diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json
index 8ac8bdad6dfd0..8757b39a0b3ac 100644
--- a/x-pack/test/tsconfig.json
+++ b/x-pack/test/tsconfig.json
@@ -3,9 +3,9 @@
"compilerOptions": {
// overhead is too significant
"incremental": false,
- "types": ["node", "flot"]
+ "types": ["node"]
},
- "include": ["**/*", "../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*"],
+ "include": ["**/*", "../../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*"],
"references": [
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../../src/plugins/bfetch/tsconfig.json" },
diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json
deleted file mode 100644
index aaf014ea6165c..0000000000000
--- a/x-pack/tsconfig.json
+++ /dev/null
@@ -1,117 +0,0 @@
-{
- "extends": "../tsconfig.base.json",
- "include": [
- "mocks.ts",
- "typings/**/*",
- "tasks/**/*",
- "plugins/cases/**/*",
- "plugins/lists/**/*",
- "plugins/security_solution/**/*"
- ],
- "exclude": [
- "test/**/*",
- "plugins/apm/e2e/cypress/**/*",
- "plugins/apm/ftr_e2e/**/*",
- "plugins/apm/scripts/**/*",
- "plugins/security_solution/cypress/**/*"
- ],
- "compilerOptions": {
- // overhead is too significant
- "incremental": false
- },
- "references": [
- { "path": "../src/core/tsconfig.json" },
- { "path": "../src/plugins/bfetch/tsconfig.json" },
- { "path": "../src/plugins/charts/tsconfig.json" },
- { "path": "../src/plugins/console/tsconfig.json" },
- { "path": "../src/plugins/dashboard/tsconfig.json" },
- { "path": "../src/plugins/data/tsconfig.json" },
- { "path": "../src/plugins/dev_tools/tsconfig.json" },
- { "path": "../src/plugins/discover/tsconfig.json" },
- { "path": "../src/plugins/embeddable/tsconfig.json" },
- { "path": "../src/plugins/es_ui_shared/tsconfig.json" },
- { "path": "../src/plugins/expressions/tsconfig.json" },
- { "path": "../src/plugins/home/tsconfig.json" },
- { "path": "../src/plugins/index_pattern_management/tsconfig.json" },
- { "path": "../src/plugins/inspector/tsconfig.json" },
- { "path": "../src/plugins/kibana_legacy/tsconfig.json" },
- { "path": "../src/plugins/kibana_overview/tsconfig.json" },
- { "path": "../src/plugins/kibana_react/tsconfig.json" },
- { "path": "../src/plugins/kibana_usage_collection/tsconfig.json" },
- { "path": "../src/plugins/kibana_utils/tsconfig.json" },
- { "path": "../src/plugins/legacy_export/tsconfig.json" },
- { "path": "../src/plugins/management/tsconfig.json" },
- { "path": "../src/plugins/navigation/tsconfig.json" },
- { "path": "../src/plugins/newsfeed/tsconfig.json" },
- { "path": "../src/plugins/presentation_util/tsconfig.json" },
- { "path": "../src/plugins/saved_objects_management/tsconfig.json" },
- { "path": "../src/plugins/saved_objects_tagging_oss/tsconfig.json" },
- { "path": "../src/plugins/saved_objects/tsconfig.json" },
- { "path": "../src/plugins/security_oss/tsconfig.json" },
- { "path": "../src/plugins/share/tsconfig.json" },
- { "path": "../src/plugins/telemetry_collection_manager/tsconfig.json" },
- { "path": "../src/plugins/telemetry_management_section/tsconfig.json" },
- { "path": "../src/plugins/telemetry/tsconfig.json" },
- { "path": "../src/plugins/ui_actions/tsconfig.json" },
- { "path": "../src/plugins/url_forwarding/tsconfig.json" },
- { "path": "../src/plugins/usage_collection/tsconfig.json" },
- { "path": "./plugins/actions/tsconfig.json" },
- { "path": "./plugins/alerting/tsconfig.json" },
- { "path": "./plugins/apm/tsconfig.json" },
- { "path": "./plugins/beats_management/tsconfig.json" },
- { "path": "./plugins/canvas/tsconfig.json" },
- { "path": "./plugins/cloud/tsconfig.json" },
- { "path": "./plugins/console_extensions/tsconfig.json" },
- { "path": "./plugins/data_enhanced/tsconfig.json" },
- { "path": "./plugins/dashboard_mode/tsconfig.json" },
- { "path": "./plugins/discover_enhanced/tsconfig.json" },
- { "path": "./plugins/drilldowns/url_drilldown/tsconfig.json" },
- { "path": "./plugins/embeddable_enhanced/tsconfig.json" },
- { "path": "./plugins/encrypted_saved_objects/tsconfig.json" },
- { "path": "./plugins/enterprise_search/tsconfig.json" },
- { "path": "./plugins/event_log/tsconfig.json" },
- { "path": "./plugins/features/tsconfig.json" },
- { "path": "./plugins/file_upload/tsconfig.json" },
- { "path": "./plugins/fleet/tsconfig.json" },
- { "path": "./plugins/global_search_bar/tsconfig.json" },
- { "path": "./plugins/global_search_providers/tsconfig.json" },
- { "path": "./plugins/global_search/tsconfig.json" },
- { "path": "./plugins/graph/tsconfig.json" },
- { "path": "./plugins/grokdebugger/tsconfig.json" },
- { "path": "./plugins/infra/tsconfig.json" },
- { "path": "./plugins/ingest_pipelines/tsconfig.json" },
- { "path": "./plugins/lens/tsconfig.json" },
- { "path": "./plugins/license_management/tsconfig.json" },
- { "path": "./plugins/licensing/tsconfig.json" },
- { "path": "./plugins/logstash/tsconfig.json" },
- { "path": "./plugins/maps_legacy_licensing/tsconfig.json" },
- { "path": "./plugins/maps/tsconfig.json" },
- { "path": "./plugins/ml/tsconfig.json" },
- { "path": "./plugins/monitoring/tsconfig.json" },
- { "path": "./plugins/observability/tsconfig.json" },
- { "path": "./plugins/osquery/tsconfig.json" },
- { "path": "./plugins/painless_lab/tsconfig.json" },
- { "path": "./plugins/saved_objects_tagging/tsconfig.json" },
- { "path": "./plugins/searchprofiler/tsconfig.json" },
- { "path": "./plugins/security/tsconfig.json" },
- { "path": "./plugins/snapshot_restore/tsconfig.json" },
- { "path": "./plugins/spaces/tsconfig.json" },
- { "path": "./plugins/stack_alerts/tsconfig.json" },
- { "path": "./plugins/task_manager/tsconfig.json" },
- { "path": "./plugins/telemetry_collection_xpack/tsconfig.json" },
- { "path": "./plugins/transform/tsconfig.json" },
- { "path": "./plugins/translations/tsconfig.json" },
- { "path": "./plugins/triggers_actions_ui/tsconfig.json" },
- { "path": "./plugins/ui_actions_enhanced/tsconfig.json" },
- { "path": "./plugins/upgrade_assistant/tsconfig.json" },
- { "path": "./plugins/runtime_fields/tsconfig.json" },
- { "path": "./plugins/index_management/tsconfig.json" },
- { "path": "./plugins/watcher/tsconfig.json" },
- { "path": "./plugins/rollup/tsconfig.json" },
- { "path": "./plugins/remote_clusters/tsconfig.json" },
- { "path": "./plugins/cross_cluster_replication/tsconfig.json"},
- { "path": "./plugins/index_lifecycle_management/tsconfig.json"},
- { "path": "./plugins/uptime/tsconfig.json" },
- { "path": "./plugins/xpack_legacy/tsconfig.json" }
- ]
-}
diff --git a/x-pack/typings/@elastic/eui/index.d.ts b/x-pack/typings/@elastic/eui/index.d.ts
deleted file mode 100644
index 7664eaa20e432..0000000000000
--- a/x-pack/typings/@elastic/eui/index.d.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-// TODO: Remove once typescript definitions are in EUI
-
-declare module '@elastic/eui/lib/services' {
- export const RIGHT_ALIGNMENT: any;
-}
-
-declare module '@elastic/eui/lib/services/format' {
- export const dateFormatAliases: any;
-}
diff --git a/x-pack/typings/cytoscape_dagre.d.ts b/x-pack/typings/cytoscape_dagre.d.ts
deleted file mode 100644
index ddc991c9fbd0a..0000000000000
--- a/x-pack/typings/cytoscape_dagre.d.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.
- */
-
-declare module 'cytoscape-dagre';
diff --git a/x-pack/typings/index.d.ts b/x-pack/typings/index.d.ts
deleted file mode 100644
index 171171de5561f..0000000000000
--- a/x-pack/typings/index.d.ts
+++ /dev/null
@@ -1,33 +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.
- */
-
-declare module '*.html' {
- const template: string;
- // eslint-disable-next-line import/no-default-export
- export default template;
-}
-
-declare module '*.png' {
- const content: string;
- // eslint-disable-next-line import/no-default-export
- export default content;
-}
-
-declare module '*.svg' {
- const content: string;
- // eslint-disable-next-line import/no-default-export
- export default content;
-}
-
-declare module 'axios/lib/adapters/xhr';
-
-// Storybook references this module. It's @ts-ignored in the codebase but when
-// built into its dist it strips that out. Add it here to avoid a type checking
-// error.
-//
-// See https://github.com/storybookjs/storybook/issues/11684
-declare module 'react-syntax-highlighter/dist/cjs/create-element';
diff --git a/x-pack/typings/rison_node.d.ts b/x-pack/typings/rison_node.d.ts
deleted file mode 100644
index b24300c100d8b..0000000000000
--- a/x-pack/typings/rison_node.d.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-declare module 'rison-node' {
- export type RisonValue = undefined | null | boolean | number | string | RisonObject | RisonArray;
-
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- export interface RisonArray extends Array {}
-
- export interface RisonObject {
- [key: string]: RisonValue;
- }
-
- export const decode: (input: string) => RisonValue;
-
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export const decode_object: (input: string) => RisonObject;
-
- export const encode: (input: Input) => string;
-
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export const encode_object: (input: Input) => string;
-
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export const encode_array: (input: Input) => string;
-}
From 09f90d86510d9620c254f8bd3ebc7b05931ef73f Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Tue, 16 Mar 2021 15:22:09 +0100
Subject: [PATCH 14/24] [Canvas] Cleanup types in lib (#94517)
* fix get_legend_config error in canvas/lib/index
* convert resolve_dataurl to ts to fix canvas/lib/index failure
* convert expression_form_handler to ts to fix canvas/lib/index failure
* convert canvas lib/error into ts
* canvas: do not compile json file due to effect on performance
* remove type. it is not exported and inferred as any implicitly
* fix datatable error in lib/index.d.ts file
* fix url resolver
* case manually to avoid incompatibility error
---
.../canvas_plugin_src/functions/common/image.ts | 3 +--
.../functions/common/repeat_image.ts | 1 -
.../functions/common/revealImage.ts | 5 ++---
.../functions/server/demodata/index.ts | 2 --
.../common/lib/datatable/{index.js => index.ts} | 2 +-
.../common/lib/datatable/{query.js => query.ts} | 12 ++++++------
.../canvas/common/lib/{errors.js => errors.ts} | 14 +++++++++-----
...orm_handlers.js => expression_form_handlers.ts} | 4 +++-
.../{get_legend_config.js => get_legend_config.ts} | 13 ++++++++++---
x-pack/plugins/canvas/common/lib/index.ts | 5 -----
.../lib/{resolve_dataurl.js => resolve_dataurl.ts} | 9 ++++++---
x-pack/plugins/canvas/public/functions/pie.ts | 1 -
.../plugins/canvas/public/functions/plot/index.ts | 1 -
x-pack/plugins/canvas/shareable_runtime/types.ts | 4 +---
x-pack/plugins/canvas/tsconfig.json | 12 ++++--------
15 files changed, 43 insertions(+), 45 deletions(-)
rename x-pack/plugins/canvas/common/lib/datatable/{index.js => index.ts} (85%)
rename x-pack/plugins/canvas/common/lib/datatable/{query.js => query.ts} (73%)
rename x-pack/plugins/canvas/common/lib/{errors.js => errors.ts} (73%)
rename x-pack/plugins/canvas/common/lib/{expression_form_handlers.js => expression_form_handlers.ts} (82%)
rename x-pack/plugins/canvas/common/lib/{get_legend_config.js => get_legend_config.ts} (66%)
rename x-pack/plugins/canvas/common/lib/{resolve_dataurl.js => resolve_dataurl.ts} (75%)
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts
index 7c4b973511672..b4d067280cb69 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts
@@ -8,7 +8,6 @@
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { getFunctionHelp, getFunctionErrors } from '../../../i18n';
-// @ts-expect-error untyped local
import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl';
import { elasticLogo } from '../../lib/elastic_logo';
@@ -64,7 +63,7 @@ export function image(): ExpressionFunctionDefinition<'image', null, Arguments,
return {
type: 'image',
mode: modeStyle,
- dataurl: resolveWithMissingImage(dataurl, elasticLogo),
+ dataurl: resolveWithMissingImage(dataurl, elasticLogo) as string,
};
},
};
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts
index c685a7aab84a8..6e62139e4da0d 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts
@@ -6,7 +6,6 @@
*/
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
-// @ts-expect-error untyped local
import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl';
import { elasticOutline } from '../../lib/elastic_outline';
import { Render } from '../../../types';
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts
index 1c9f2c7c1e0f9..91d70609ab708 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts
@@ -6,7 +6,6 @@
*/
import { ExpressionFunctionDefinition, ExpressionValueRender } from 'src/plugins/expressions';
-// @ts-expect-error untyped local
import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl';
import { elasticOutline } from '../../lib/elastic_outline';
import { getFunctionHelp, getFunctionErrors } from '../../../i18n';
@@ -75,8 +74,8 @@ export function revealImage(): ExpressionFunctionDefinition<
value: {
percent,
...args,
- image: resolveWithMissingImage(args.image, elasticOutline),
- emptyImage: resolveWithMissingImage(args.emptyImage),
+ image: resolveWithMissingImage(args.image, elasticOutline) as string,
+ emptyImage: resolveWithMissingImage(args.emptyImage) as string,
},
};
},
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts
index bdaeb9cb0929f..5dc1790e67d7d 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts
@@ -7,7 +7,6 @@
import { sortBy } from 'lodash';
import { ExpressionFunctionDefinition } from 'src/plugins/expressions';
-// @ts-expect-error unconverted lib file
import { queryDatatable } from '../../../../common/lib/datatable/query';
import { DemoRows } from './demo_rows_types';
import { getDemoRows } from './get_demo_rows';
@@ -62,7 +61,6 @@ export function demodata(): ExpressionFunctionDefinition<
{ id: 'project', name: 'project', meta: { type: 'string' } },
{ id: 'percent_uptime', name: 'percent_uptime', meta: { type: 'number' } },
],
- // @ts-expect-error invalid json mock
rows: sortBy(demoRows, 'time'),
};
} else if (args.type === DemoRows.SHIRTS) {
diff --git a/x-pack/plugins/canvas/common/lib/datatable/index.js b/x-pack/plugins/canvas/common/lib/datatable/index.ts
similarity index 85%
rename from x-pack/plugins/canvas/common/lib/datatable/index.js
rename to x-pack/plugins/canvas/common/lib/datatable/index.ts
index 66ede766e4741..a1bb7d690ec4c 100644
--- a/x-pack/plugins/canvas/common/lib/datatable/index.js
+++ b/x-pack/plugins/canvas/common/lib/datatable/index.ts
@@ -5,4 +5,4 @@
* 2.0.
*/
-export * from './query';
+export { queryDatatable } from './query';
diff --git a/x-pack/plugins/canvas/common/lib/datatable/query.js b/x-pack/plugins/canvas/common/lib/datatable/query.ts
similarity index 73%
rename from x-pack/plugins/canvas/common/lib/datatable/query.js
rename to x-pack/plugins/canvas/common/lib/datatable/query.ts
index da2219a2e2bcd..748810432bd22 100644
--- a/x-pack/plugins/canvas/common/lib/datatable/query.js
+++ b/x-pack/plugins/canvas/common/lib/datatable/query.ts
@@ -4,8 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-
-export function queryDatatable(datatable, query) {
+import type { Datatable } from '../../../types';
+export function queryDatatable(datatable: Datatable, query: Record) {
if (query.size) {
datatable = {
...datatable,
@@ -14,17 +14,17 @@ export function queryDatatable(datatable, query) {
}
if (query.and) {
- query.and.forEach((filter) => {
+ query.and.forEach((filter: any) => {
// handle exact matches
if (filter.filterType === 'exactly') {
- datatable.rows = datatable.rows.filter((row) => {
+ datatable.rows = datatable.rows.filter((row: any) => {
return row[filter.column] === filter.value;
});
}
// handle time filters
if (filter.filterType === 'time') {
- const columnNames = datatable.columns.map((col) => col.name);
+ const columnNames = datatable.columns.map((col: any) => col.name);
// remove row if no column match
if (!columnNames.includes(filter.column)) {
@@ -32,7 +32,7 @@ export function queryDatatable(datatable, query) {
return;
}
- datatable.rows = datatable.rows.filter((row) => {
+ datatable.rows = datatable.rows.filter((row: any) => {
const fromTime = new Date(filter.from).getTime();
const toTime = new Date(filter.to).getTime();
const rowTime = new Date(row[filter.column]).getTime();
diff --git a/x-pack/plugins/canvas/common/lib/errors.js b/x-pack/plugins/canvas/common/lib/errors.ts
similarity index 73%
rename from x-pack/plugins/canvas/common/lib/errors.js
rename to x-pack/plugins/canvas/common/lib/errors.ts
index 93678c308b5c4..f9039e7aa5ba6 100644
--- a/x-pack/plugins/canvas/common/lib/errors.js
+++ b/x-pack/plugins/canvas/common/lib/errors.ts
@@ -5,8 +5,10 @@
* 2.0.
*/
+type NewableError = (...args: any[]) => Error;
+
// helper to correctly set the prototype of custom error constructor
-function setErrorPrototype(CustomError) {
+function setErrorPrototype(CustomError: NewableError) {
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
@@ -20,15 +22,17 @@ function setErrorPrototype(CustomError) {
}
// helper to create a custom error by name
-function createError(name) {
- function CustomError(...args) {
+function createError(name: string) {
+ function CustomError(...args: any[]) {
const instance = new Error(...args);
- instance.name = this.name = name;
+ // @ts-expect-error this has not type annotation
+ const self = this as any;
+ instance.name = self.name = name;
if (Error.captureStackTrace) {
Error.captureStackTrace(instance, CustomError);
} else {
- Object.defineProperty(this, 'stack', {
+ Object.defineProperty(self, 'stack', {
get() {
return instance.stack;
},
diff --git a/x-pack/plugins/canvas/common/lib/expression_form_handlers.js b/x-pack/plugins/canvas/common/lib/expression_form_handlers.ts
similarity index 82%
rename from x-pack/plugins/canvas/common/lib/expression_form_handlers.js
rename to x-pack/plugins/canvas/common/lib/expression_form_handlers.ts
index ac6ef62d3bba8..18e32eb635bb3 100644
--- a/x-pack/plugins/canvas/common/lib/expression_form_handlers.js
+++ b/x-pack/plugins/canvas/common/lib/expression_form_handlers.ts
@@ -6,12 +6,14 @@
*/
export class ExpressionFormHandlers {
+ public destroy: () => void;
+ public done: () => void;
constructor() {
this.destroy = () => {};
this.done = () => {};
}
- onDestroy(fn) {
+ onDestroy(fn: () => void) {
this.destroy = fn;
}
}
diff --git a/x-pack/plugins/canvas/common/lib/get_legend_config.js b/x-pack/plugins/canvas/common/lib/get_legend_config.ts
similarity index 66%
rename from x-pack/plugins/canvas/common/lib/get_legend_config.js
rename to x-pack/plugins/canvas/common/lib/get_legend_config.ts
index 6f143b26ab783..ae27d1449f140 100644
--- a/x-pack/plugins/canvas/common/lib/get_legend_config.js
+++ b/x-pack/plugins/canvas/common/lib/get_legend_config.ts
@@ -5,7 +5,15 @@
* 2.0.
*/
-export const getLegendConfig = (legend, size) => {
+import { Legend } from '../../types';
+const acceptedPositions: Legend[] = [
+ Legend.NORTH_WEST,
+ Legend.SOUTH_WEST,
+ Legend.NORTH_EAST,
+ Legend.SOUTH_EAST,
+];
+
+export const getLegendConfig = (legend: boolean | Legend, size: number) => {
if (!legend || size < 2) {
return { show: false };
}
@@ -16,8 +24,7 @@ export const getLegendConfig = (legend, size) => {
labelBoxBorderColor: 'transparent',
};
- const acceptedPositions = ['nw', 'ne', 'sw', 'se'];
-
+ // @ts-expect-error
config.position = !legend || acceptedPositions.includes(legend) ? legend : 'ne';
return config;
diff --git a/x-pack/plugins/canvas/common/lib/index.ts b/x-pack/plugins/canvas/common/lib/index.ts
index f7b4de235f353..afce09c6d5ee9 100644
--- a/x-pack/plugins/canvas/common/lib/index.ts
+++ b/x-pack/plugins/canvas/common/lib/index.ts
@@ -5,26 +5,21 @@
* 2.0.
*/
-// @ts-expect-error missing local definition
export * from './datatable';
export * from './autocomplete';
export * from './constants';
export * from './dataurl';
-// @ts-expect-error missing local definition
export * from './errors';
-// @ts-expect-error missing local definition
export * from './expression_form_handlers';
export * from './fetch';
export * from './fonts';
export * from './get_field_type';
-// @ts-expect-error missing local definition
export * from './get_legend_config';
export * from './hex_to_rgb';
export * from './httpurl';
export * from './missing_asset';
export * from './palettes';
export * from './pivot_object_array';
-// @ts-expect-error missing local definition
export * from './resolve_dataurl';
export * from './unquote_string';
export * from './url';
diff --git a/x-pack/plugins/canvas/common/lib/resolve_dataurl.js b/x-pack/plugins/canvas/common/lib/resolve_dataurl.ts
similarity index 75%
rename from x-pack/plugins/canvas/common/lib/resolve_dataurl.js
rename to x-pack/plugins/canvas/common/lib/resolve_dataurl.ts
index 92bb69ff9c7fb..79e49c0595355 100644
--- a/x-pack/plugins/canvas/common/lib/resolve_dataurl.js
+++ b/x-pack/plugins/canvas/common/lib/resolve_dataurl.ts
@@ -14,13 +14,16 @@ import { missingImage } from '../../common/lib/missing_asset';
* For example:
* [{"type":"expression","chain":[{"type":"function","function":"asset","arguments":{"_":["..."]}}]}]
*/
-export const resolveFromArgs = (args, defaultDataurl = null) => {
+export const resolveFromArgs = (args: any, defaultDataurl: string | null = null): string => {
const dataurl = get(args, 'dataurl.0', null);
return isValidUrl(dataurl) ? dataurl : defaultDataurl;
};
-export const resolveWithMissingImage = (img, alt = null) => {
- if (isValidUrl(img)) {
+export const resolveWithMissingImage = (
+ img: string | null,
+ alt: string | null = null
+): string | null => {
+ if (img !== null && isValidUrl(img)) {
return img;
}
if (img === null) {
diff --git a/x-pack/plugins/canvas/public/functions/pie.ts b/x-pack/plugins/canvas/public/functions/pie.ts
index 31da3e074152f..0840667302ebe 100644
--- a/x-pack/plugins/canvas/public/functions/pie.ts
+++ b/x-pack/plugins/canvas/public/functions/pie.ts
@@ -7,7 +7,6 @@
import { get, keyBy, map, groupBy } from 'lodash';
import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
-// @ts-expect-error untyped local
import { getLegendConfig } from '../../common/lib/get_legend_config';
import { getFunctionHelp } from '../../i18n';
import {
diff --git a/x-pack/plugins/canvas/public/functions/plot/index.ts b/x-pack/plugins/canvas/public/functions/plot/index.ts
index 6dff62b7d7cd7..47b9212bbc4c0 100644
--- a/x-pack/plugins/canvas/public/functions/plot/index.ts
+++ b/x-pack/plugins/canvas/public/functions/plot/index.ts
@@ -9,7 +9,6 @@ import { set } from '@elastic/safer-lodash-set';
import { groupBy, get, keyBy, map, sortBy } from 'lodash';
import { ExpressionFunctionDefinition, Style } from 'src/plugins/expressions';
import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
-// @ts-expect-error untyped local
import { getLegendConfig } from '../../../common/lib/get_legend_config';
import { getFlotAxisConfig } from './get_flot_axis_config';
import { getFontSpec } from './get_font_spec';
diff --git a/x-pack/plugins/canvas/shareable_runtime/types.ts b/x-pack/plugins/canvas/shareable_runtime/types.ts
index 14449ca6d9a93..ac8f140b7f11d 100644
--- a/x-pack/plugins/canvas/shareable_runtime/types.ts
+++ b/x-pack/plugins/canvas/shareable_runtime/types.ts
@@ -6,8 +6,6 @@
*/
import { RefObject } from 'react';
-// @ts-expect-error Unlinked Webpack Type
-import ContainerStyle from 'types/interpreter';
import { SavedObject, SavedObjectAttributes } from 'src/core/public';
import { ElementPosition, CanvasPage, CanvasWorkpad, RendererSpec } from '../types';
@@ -52,7 +50,7 @@ export interface CanvasRenderable {
state: 'ready' | 'error';
value: {
as: string;
- containerStyle: ContainerStyle;
+ containerStyle: any;
css: string;
type: 'render';
value: any;
diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json
index 3e3986082e207..487b68ba3542b 100644
--- a/x-pack/plugins/canvas/tsconfig.json
+++ b/x-pack/plugins/canvas/tsconfig.json
@@ -5,7 +5,10 @@
"outDir": "./target/types",
"emitDeclarationOnly": true,
"declaration": true,
- "declarationMap": true
+ "declarationMap": true,
+
+ // the plugin contains some heavy json files
+ "resolveJsonModule": false,
},
"include": [
"../../../typings/**/*",
@@ -19,13 +22,6 @@
"storybook/**/*",
"tasks/mocks/*",
"types/**/*",
- "**/*.json",
- ],
- "exclude": [
- // these files are too large and upset tsc, so we exclude them
- "server/sample_data/*.json",
- "canvas_plugin_src/functions/server/demodata/*.json",
- "shareable_runtime/test/workpads/*.json",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
From c9d1dbf599669fdd3e548c682d4323842197ad19 Mon Sep 17 00:00:00 2001
From: Vadim Yakhin
Date: Tue, 16 Mar 2021 11:48:07 -0300
Subject: [PATCH 15/24] [Workplace Search] Misc bugfixes (#94612)
* Fix incorrect copy
* Fix incorrect copy
* Update Box icon to match other icon sizes
* Add missing spacer on connector configuration screen
* Add missing spacer to Manage Source modal on Group page
* Remove shadows on Security page
* Align the last column content in tables to the right
* Fix colors on save custom source page
"Secondary" is greenish in new version is EUI, we need "subdued"
* Fix link to personal dashboard
* Add missing breadcrumbs to Security and Settings pages
* Deduplicate Security tests on Basic and Platinum licenses
* Prevent range slider from shifting to left when priority is 10
When priority is 10, the number become wider and it pushes the range slider to the left. This commit is a quick fix for that. We could improve it later by adding a proper input.
* Fix i18n duplicate ID
* Revert "Fix link to personal dashboard"
This reverts commit 5fc3ad2937f3c3236ed140c994daf2edd178a555.
---
.../components/shared/assets/source_icons/box.svg | 2 +-
.../components/shared/source_row/source_row.scss | 4 ----
.../components/shared/source_row/source_row.tsx | 2 +-
.../public/applications/workplace_search/constants.ts | 2 +-
.../components/add_source/save_config.tsx | 1 +
.../components/add_source/save_custom.tsx | 8 ++++----
.../views/content_sources/components/overview.tsx | 4 ++--
.../views/content_sources/components/source_content.tsx | 6 ++++--
.../workplace_search/views/content_sources/constants.ts | 4 ++--
.../views/groups/components/group_row.tsx | 2 +-
.../groups/components/group_source_prioritization.tsx | 2 +-
.../views/groups/components/shared_sources_modal.tsx | 2 ++
.../views/security/components/private_sources_table.tsx | 4 ++--
.../workplace_search/views/security/security.test.tsx | 9 +++++----
.../workplace_search/views/security/security.tsx | 5 ++++-
.../views/settings/settings_router.test.tsx | 2 ++
.../workplace_search/views/settings/settings_router.tsx | 5 +++++
17 files changed, 38 insertions(+), 26 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg
index 827f8cf0a55ec..b1b542eadd59c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.scss
index a099b974a0d41..fb8a47d134269 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.scss
@@ -8,10 +8,6 @@
font-weight: 500;
}
- &__actions {
- width: 100px;
- }
-
&__actions a {
opacity: 1.0;
pointer-events: auto;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx
index 6cfc68b45ee3c..a6b2878de6449 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx
@@ -164,7 +164,7 @@ export const SourceRow: React.FC = ({
/>
)}
-
+
{showFix && {fixLink} }
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
index cdfd07b07de91..ddec0d9d13873 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
@@ -574,7 +574,7 @@ export const CUSTOMIZE_HEADER_DESCRIPTION = i18n.translate(
export const CUSTOMIZE_NAME_LABEL = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.customize.name.label',
{
- defaultMessage: 'Personalize general organization settings.',
+ defaultMessage: 'Organization name',
}
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_config.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_config.tsx
index 956d5143ef2c5..053a3b6b0e6bb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_config.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_config.tsx
@@ -224,6 +224,7 @@ export const SaveConfig: React.FC = ({
return (
<>
{header}
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx
index b42bd674109fe..5aae4b352a1fb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/save_custom.tsx
@@ -102,7 +102,7 @@ export const SaveCustom: React.FC = ({
{SAVE_CUSTOM_API_KEYS_TITLE}
-
+
{SAVE_CUSTOM_API_KEYS_BODY}
@@ -126,7 +126,7 @@ export const SaveCustom: React.FC = ({
{SAVE_CUSTOM_VISUAL_WALKTHROUGH_TITLE}
-
+
= ({
{SAVE_CUSTOM_STYLING_RESULTS_TITLE}
-
+
= ({
{SAVE_CUSTOM_DOC_PERMISSIONS_TITLE}
-
+
{
{EVENT_HEADER}
{!custom && {STATUS_HEADER} }
- {TIME_HEADER}
+ {TIME_HEADER}
{activities.map(({ details: activityDetails, event, time, status }, i) => (
@@ -203,7 +203,7 @@ export const Overview: React.FC = () => {
)}
-
+
{time}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
index 3dd8ad1dc7899..1a6d97bbf75ba 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx
@@ -151,7 +151,9 @@ export const SourceContent: React.FC = () => {
)}
- {moment(updated).format('M/D/YYYY, h:mm:ss A')}
+
+ {moment(updated).format('M/D/YYYY, h:mm:ss A')}
+
);
};
@@ -164,7 +166,7 @@ export const SourceContent: React.FC = () => {
{TITLE_HEADING}
{startCase(urlField)}
- {LAST_UPDATED_HEADING}
+ {LAST_UPDATED_HEADING}
{contentItems.map(contentItem)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
index 3e1290292704e..aa6d4da99ea40 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
@@ -300,9 +300,9 @@ export const SOURCE_REMOVE_TITLE = i18n.translate(
);
export const SOURCE_REMOVE_DESCRIPTION = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.config.description',
+ 'xpack.enterpriseSearch.workplaceSearch.sources.remove.description',
{
- defaultMessage: 'Edit content source connector settings to change.',
+ defaultMessage: 'This action cannot be undone.',
}
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx
index 5e89d4491d597..204d8f5655172 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx
@@ -91,7 +91,7 @@ export const GroupRow: React.FC = ({
)}
-
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_source_prioritization.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_source_prioritization.tsx
index 9b131e730b937..df7435bd25461 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_source_prioritization.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_source_prioritization.tsx
@@ -164,7 +164,7 @@ export const GroupSourcePrioritization: React.FC = () => {
}
/>
-
+
{activeSourcePriorities[id]}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/shared_sources_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/shared_sources_modal.tsx
index 2fcd880318a27..631c4f80f36b0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/shared_sources_modal.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/shared_sources_modal.tsx
@@ -9,6 +9,7 @@ import React from 'react';
import { useActions, useValues } from 'kea';
+import { EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { GroupLogic } from '../group_logic';
@@ -53,6 +54,7 @@ export const SharedSourcesModal: React.FC = () => {
values: { groupName: group.name },
})}
+
= ({
const emptyState = (
<>
-
+
{isRemote ? REMOTE_SOURCES_EMPTY_TABLE_TITLE : STANDARD_SOURCES_EMPTY_TABLE_TITLE}
@@ -175,7 +175,7 @@ export const PrivateSourcesTable: React.FC = ({
);
return (
-
+
{sectionHeading}
{hasSources && sourcesTable}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.test.tsx
index 51346a69eeec2..346994ac557f9 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.test.tsx
@@ -14,6 +14,8 @@ import { shallow } from 'enzyme';
import { EuiSwitch, EuiConfirmModal } from '@elastic/eui';
+import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
+
import { Loading } from '../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
import { ViewContentHeader } from '../../components/shared/view_content_header';
@@ -53,20 +55,19 @@ describe('Security', () => {
});
});
- it('renders on Basic license', () => {
+ it('renders', () => {
setMockValues({ ...mockValues, hasPlatinumLicense: false });
const wrapper = shallow( );
+ expect(wrapper.find(SetPageChrome)).toHaveLength(1);
expect(wrapper.find(UnsavedChangesPrompt)).toHaveLength(1);
expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
expect(wrapper.find(EuiSwitch).prop('disabled')).toEqual(true);
});
- it('renders on Platinum license', () => {
+ it('does not disable switch on Platinum license', () => {
const wrapper = shallow( );
- expect(wrapper.find(UnsavedChangesPrompt)).toHaveLength(1);
- expect(wrapper.find(ViewContentHeader)).toHaveLength(1);
expect(wrapper.find(EuiSwitch).prop('disabled')).toEqual(false);
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.tsx
index a81ac93ab69dd..669015794baef 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/security/security.tsx
@@ -23,6 +23,7 @@ import {
} from '@elastic/eui';
import { FlashMessages } from '../../../shared/flash_messages';
+import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { LicensingLogic } from '../../../shared/licensing';
import { Loading } from '../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
@@ -40,6 +41,7 @@ import {
PRIVATE_PLATINUM_LICENSE_CALLOUT,
CONFIRM_CHANGES_TEXT,
PRIVATE_SOURCES_UPDATE_CONFIRMATION_TEXT,
+ NAV,
} from '../../constants';
import { PrivateSourcesTable } from './components/private_sources_table';
@@ -114,7 +116,7 @@ export const Security: React.FC = () => {
);
const allSourcesToggle = (
-
+
{
return (
<>
+
{
const wrapper = shallow( );
expect(wrapper.find(FlashMessages)).toHaveLength(1);
+ expect(wrapper.find(SetPageChrome)).toHaveLength(3);
expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(NUM_ROUTES);
expect(wrapper.find(Redirect)).toHaveLength(1);
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx
index 34dcc48621a2e..e6264103df6d8 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx
@@ -11,6 +11,8 @@ import { Redirect, Route, Switch } from 'react-router-dom';
import { useActions } from 'kea';
import { FlashMessages } from '../../../shared/flash_messages';
+import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
+import { NAV } from '../../constants';
import {
ORG_SETTINGS_PATH,
ORG_SETTINGS_CUSTOMIZE_PATH,
@@ -38,12 +40,15 @@ export const SettingsRouter: React.FC = () => {
+
+
+
{staticSourceData.map(({ editPath }, i) => (
From 310194193aad34fee6852a4bd195562fb494c7ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Loix?=
Date: Tue, 16 Mar 2021 15:22:24 +0000
Subject: [PATCH 16/24] [ILM] Use global field to set the snapshot repository
(#94602)
---
.../features/searchable_snapshots.test.ts | 48 ++++++++++-
.../phases/cold_phase/cold_phase.tsx | 4 +-
.../phases/delete_phase/delete_phase.tsx | 12 ++-
.../components/phases/hot_phase/hot_phase.tsx | 4 +-
.../min_age_field/min_age_field.tsx | 4 +-
.../repository_combobox_field.tsx | 66 ++++++++++++++
.../searchable_snapshot_field.tsx | 85 +++++--------------
.../phases/warm_phase/warm_phase.tsx | 4 +-
.../timeline/timeline.container.tsx | 4 +-
.../sections/edit_policy/constants.ts | 6 ++
.../sections/edit_policy/edit_policy.tsx | 49 +++++++----
.../edit_policy/form/components/form.tsx | 25 +++---
..._context.tsx => configuration_context.tsx} | 33 +++----
.../sections/edit_policy/form/deserializer.ts | 22 ++++-
.../form/deserializer_and_serializer.test.ts | 4 +-
.../form/global_fields_context.tsx | 54 ++++++++++++
.../sections/edit_policy/form/index.ts | 11 ++-
.../form/phase_timings_context.tsx | 25 +++---
.../sections/edit_policy/form/schema.ts | 27 ++++--
.../edit_policy/form/serializer/serializer.ts | 23 ++++-
...absolute_timing_to_relative_timing.test.ts | 4 +-
.../edit_policy/lib/get_default_repository.ts | 20 +++++
.../sections/edit_policy/lib/index.ts | 2 +
.../application/sections/edit_policy/types.ts | 4 +-
.../public/shared_imports.ts | 1 +
25 files changed, 373 insertions(+), 168 deletions(-)
create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/repository_combobox_field.tsx
rename x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/{configuration_issues_context.tsx => configuration_context.tsx} (66%)
create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/global_fields_context.tsx
create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/get_default_repository.ts
diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts
index 44e03564cb89a..a570c817cfe1b 100644
--- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts
+++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts
@@ -67,6 +67,46 @@ describe(' searchable snapshots', () => {
expect(actions.hot.searchableSnapshotsExists()).toBeTruthy();
});
+ test('should set the repository from previously defined repository', async () => {
+ const { actions } = testBed;
+
+ const repository = 'myRepo';
+ await actions.hot.setSearchableSnapshot(repository);
+ await actions.cold.enable(true);
+ await actions.cold.toggleSearchableSnapshot(true);
+ await actions.frozen.enable(true);
+
+ await actions.savePolicy();
+ const latestRequest = server.requests[server.requests.length - 1];
+ expect(latestRequest.method).toBe('POST');
+ expect(latestRequest.url).toBe('/api/index_lifecycle_management/policies');
+ const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body);
+
+ expect(reqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe(repository);
+ expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe(repository);
+ expect(reqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe(repository);
+ });
+
+ test('should update the repository in all searchable snapshot actions', async () => {
+ const { actions } = testBed;
+
+ await actions.hot.setSearchableSnapshot('myRepo');
+ await actions.cold.enable(true);
+ await actions.cold.toggleSearchableSnapshot(true);
+ await actions.frozen.enable(true);
+
+ // We update the repository in one phase
+ await actions.frozen.setSearchableSnapshot('changed');
+ await actions.savePolicy();
+ const latestRequest = server.requests[server.requests.length - 1];
+ const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body);
+
+ // And all phases should be updated
+ expect(reqBody.phases.hot.actions.searchable_snapshot.snapshot_repository).toBe('changed');
+ expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toBe('changed');
+ expect(reqBody.phases.frozen.actions.searchable_snapshot.snapshot_repository).toBe('changed');
+ });
+
describe('on cloud', () => {
describe('new policy', () => {
beforeEach(async () => {
@@ -86,6 +126,7 @@ describe(' searchable snapshots', () => {
const { component } = testBed;
component.update();
});
+
test('defaults searchable snapshot to true on cloud', async () => {
const { find, actions } = testBed;
await actions.cold.enable(true);
@@ -112,14 +153,17 @@ describe(' searchable snapshots', () => {
const { component } = testBed;
component.update();
});
+
test('correctly sets snapshot repository default to "found-snapshots"', async () => {
const { actions } = testBed;
await actions.cold.enable(true);
await actions.cold.toggleSearchableSnapshot(true);
await actions.savePolicy();
const latestRequest = server.requests[server.requests.length - 1];
- const request = JSON.parse(JSON.parse(latestRequest.requestBody).body);
- expect(request.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual(
+ expect(latestRequest.method).toBe('POST');
+ expect(latestRequest.url).toBe('/api/index_lifecycle_management/policies');
+ const reqBody = JSON.parse(JSON.parse(latestRequest.requestBody).body);
+ expect(reqBody.phases.cold.actions.searchable_snapshot.snapshot_repository).toEqual(
'found-snapshots'
);
});
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx
index bc22516e6c996..72651778f403e 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx
@@ -8,7 +8,7 @@
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
-import { useConfigurationIssues } from '../../../form';
+import { useConfiguration } from '../../../form';
import {
DataTierAllocationField,
SearchableSnapshotField,
@@ -29,7 +29,7 @@ const i18nTexts = {
};
export const ColdPhase: FunctionComponent = () => {
- const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues();
+ const { isUsingSearchableSnapshotInHotPhase } = useConfiguration();
return (
}>
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/delete_phase/delete_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/delete_phase/delete_phase.tsx
index 6c96178c86b5b..7b613757fa474 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/delete_phase/delete_phase.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/delete_phase/delete_phase.tsx
@@ -20,18 +20,16 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { useFormData } from '../../../../../../shared_imports';
-
import { i18nTexts } from '../../../i18n_texts';
-
-import { usePhaseTimings } from '../../../form';
-
-import { MinAgeField, SnapshotPoliciesField } from '../shared_fields';
-import './delete_phase.scss';
+import { usePhaseTimings, globalFields } from '../../../form';
import { PhaseIcon } from '../../phase_icon';
+import { MinAgeField, SnapshotPoliciesField } from '../shared_fields';
import { PhaseErrorIndicator } from '../phase/phase_error_indicator';
+import './delete_phase.scss';
+
const formFieldPaths = {
- enabled: '_meta.delete.enabled',
+ enabled: globalFields.deleteEnabled.path,
};
export const DeletePhase: FunctionComponent = () => {
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx
index 6d4e2750bb2e8..ea345009b230b 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx
@@ -23,7 +23,7 @@ import { useFormData, SelectField, NumericField } from '../../../../../../shared
import { i18nTexts } from '../../../i18n_texts';
-import { ROLLOVER_EMPTY_VALIDATION, useConfigurationIssues, UseField } from '../../../form';
+import { ROLLOVER_EMPTY_VALIDATION, useConfiguration, UseField } from '../../../form';
import { useEditPolicyContext } from '../../../edit_policy_context';
@@ -47,7 +47,7 @@ export const HotPhase: FunctionComponent = () => {
const [formData] = useFormData({
watch: isUsingDefaultRolloverPath,
});
- const { isUsingRollover } = useConfigurationIssues();
+ const { isUsingRollover } = useConfiguration();
const isUsingDefaultRollover: boolean = get(formData, isUsingDefaultRolloverPath);
const [showEmptyRolloverFieldsError, setShowEmptyRolloverFieldsError] = useState(false);
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/min_age_field/min_age_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/min_age_field/min_age_field.tsx
index 04b756dc23559..3fe2f08cb4066 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/min_age_field/min_age_field.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/min_age_field/min_age_field.tsx
@@ -22,7 +22,7 @@ import {
import { getFieldValidityAndErrorMessage } from '../../../../../../../shared_imports';
-import { UseField, useConfigurationIssues } from '../../../../form';
+import { UseField, useConfiguration } from '../../../../form';
import { getUnitsAriaLabelForPhase, getTimingLabelForPhase } from './util';
@@ -81,7 +81,7 @@ interface Props {
}
export const MinAgeField: FunctionComponent = ({ phase }): React.ReactElement => {
- const { isUsingRollover } = useConfigurationIssues();
+ const { isUsingRollover } = useConfiguration();
return (
{(field) => {
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/repository_combobox_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/repository_combobox_field.tsx
new file mode 100644
index 0000000000000..a5a9d8d492682
--- /dev/null
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/repository_combobox_field.tsx
@@ -0,0 +1,66 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import React, { useEffect, useRef } from 'react';
+import { EuiComboBoxOptionOption } from '@elastic/eui';
+
+import { ComboBoxField, FieldHook } from '../../../../../../../shared_imports';
+import { useGlobalFields } from '../../../../form';
+
+interface PropsRepositoryCombobox {
+ field: FieldHook;
+ isLoading: boolean;
+ repos: string[];
+ noSuggestions: boolean;
+ globalRepository: string;
+}
+
+export const RepositoryComboBoxField = ({
+ field,
+ isLoading,
+ repos,
+ noSuggestions,
+ globalRepository,
+}: PropsRepositoryCombobox) => {
+ const isMounted = useRef(false);
+ const { setValue } = field;
+ const {
+ searchableSnapshotRepo: { setValue: setSearchableSnapshotRepository },
+ } = useGlobalFields();
+
+ useEffect(() => {
+ // We keep our phase searchable action field in sync
+ // with the default repository field declared globally for the policy
+ if (isMounted.current) {
+ setValue(Boolean(globalRepository.trim()) ? [globalRepository] : []);
+ }
+ isMounted.current = true;
+ }, [setValue, globalRepository]);
+
+ return (
+ ({ label: repo, value: repo })),
+ singleSelection: { asPlainText: true },
+ isLoading,
+ noSuggestions,
+ onCreateOption: (newOption: string) => {
+ setSearchableSnapshotRepository(newOption);
+ },
+ onChange: (options: EuiComboBoxOptionOption[]) => {
+ if (options.length > 0) {
+ setSearchableSnapshotRepository(options[0].label);
+ } else {
+ setSearchableSnapshotRepository('');
+ }
+ },
+ }}
+ />
+ );
+};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx
index 816e1aaec31d7..4cef7615a2d8d 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx
@@ -5,24 +5,18 @@
* 2.0.
*/
+import React, { FunctionComponent, useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
-import React, { FunctionComponent, useState, useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
-import {
- EuiComboBoxOptionOption,
- EuiTextColor,
- EuiSpacer,
- EuiCallOut,
- EuiLink,
-} from '@elastic/eui';
-
-import { ComboBoxField, useKibana, useFormData } from '../../../../../../../shared_imports';
+import { EuiTextColor, EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui';
+import { useKibana, useFormData } from '../../../../../../../shared_imports';
import { useEditPolicyContext } from '../../../../edit_policy_context';
-import { useConfigurationIssues, UseField, searchableSnapshotFields } from '../../../../form';
+import { useConfiguration, UseField, globalFields } from '../../../../form';
import { FieldLoadingError, DescribedFormRow, LearnMoreLink } from '../../../';
import { SearchableSnapshotDataProvider } from './searchable_snapshot_data_provider';
+import { RepositoryComboBoxField } from './repository_combobox_field';
import './_searchable_snapshot_field.scss';
@@ -31,12 +25,6 @@ export interface Props {
canBeDisabled?: boolean;
}
-/**
- * This repository is provisioned by Elastic Cloud and will always
- * exist as a "managed" repository.
- */
-const CLOUD_DEFAULT_REPO = 'found-snapshots';
-
const geti18nTexts = (phase: Props['phase']) => ({
title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldTitle', {
defaultMessage: 'Searchable snapshot',
@@ -71,13 +59,15 @@ export const SearchableSnapshotField: FunctionComponent = ({
services: { cloud },
} = useKibana();
const { getUrlForApp, policy, license, isNewPolicy } = useEditPolicyContext();
- const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues();
+ const { isUsingSearchableSnapshotInHotPhase } = useConfiguration();
const searchableSnapshotRepoPath = `phases.${phase}.actions.searchable_snapshot.snapshot_repository`;
- const [formData] = useFormData({ watch: searchableSnapshotRepoPath });
- const searchableSnapshotRepo = get(formData, searchableSnapshotRepoPath);
+ const [formData] = useFormData({
+ watch: globalFields.searchableSnapshotRepo.path,
+ });
+ const searchableSnapshotGlobalRepo = get(formData, globalFields.searchableSnapshotRepo.path);
const isColdPhase = phase === 'cold';
const isFrozenPhase = phase === 'frozen';
const isColdOrFrozenPhase = isColdPhase || isFrozenPhase;
@@ -164,7 +154,10 @@ export const SearchableSnapshotField: FunctionComponent = ({
/>
);
- } else if (searchableSnapshotRepo && !repos.includes(searchableSnapshotRepo)) {
+ } else if (
+ searchableSnapshotGlobalRepo &&
+ !repos.includes(searchableSnapshotGlobalRepo)
+ ) {
calloutContent = (
= ({
return (
-
- config={{
- ...searchableSnapshotFields.snapshot_repository,
- defaultValue: cloud?.isCloudEnabled ? CLOUD_DEFAULT_REPO : undefined,
- }}
+
- {(field) => {
- const singleSelectionArray: [selectedSnapshot?: string] = field.value
- ? [field.value]
- : [];
-
- return (
- ({ label: repo, value: repo })),
- singleSelection: { asPlainText: true },
- isLoading,
- noSuggestions: !!(error || repos.length === 0),
- onCreateOption: (newOption: string) => {
- field.setValue(newOption);
- },
- onChange: (options: EuiComboBoxOptionOption[]) => {
- if (options.length > 0) {
- field.setValue(options[0].label);
- } else {
- field.setValue('');
- }
- },
- }}
- />
- );
+ defaultValue={!!searchableSnapshotGlobalRepo ? [searchableSnapshotGlobalRepo] : []}
+ component={RepositoryComboBoxField}
+ componentProps={{
+ globalRepository: searchableSnapshotGlobalRepo,
+ isLoading,
+ repos,
+ noSuggestions: !!(error || repos.length === 0),
}}
-
+ />
{calloutContent && (
<>
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx
index 577dab6804147..d082489c4b918 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx
@@ -8,7 +8,7 @@
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
-import { useConfigurationIssues } from '../../../form';
+import { useConfiguration } from '../../../form';
import {
ForcemergeField,
@@ -30,7 +30,7 @@ const i18nTexts = {
};
export const WarmPhase: FunctionComponent = () => {
- const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues();
+ const { isUsingSearchableSnapshotInHotPhase } = useConfiguration();
return (
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx
index 88d9d2de03d89..d5cbb267c77c3 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx
@@ -11,7 +11,7 @@ import { useFormData } from '../../../../../shared_imports';
import { formDataToAbsoluteTimings } from '../../lib';
-import { useConfigurationIssues } from '../../form';
+import { useConfiguration } from '../../form';
import { FormInternal } from '../../types';
@@ -20,7 +20,7 @@ import { Timeline as ViewComponent } from './timeline';
export const Timeline: FunctionComponent = () => {
const [formData] = useFormData();
const timings = formDataToAbsoluteTimings(formData);
- const { isUsingRollover } = useConfigurationIssues();
+ const { isUsingRollover } = useConfiguration();
return (
= ({ history }) => {
license,
} = useEditPolicyContext();
- const serializer = useMemo(() => {
- return createSerializer(isNewPolicy ? undefined : currentPolicy);
- }, [isNewPolicy, currentPolicy]);
+ const {
+ services: { cloud },
+ } = useKibana();
const [saveAsNew, setSaveAsNew] = useState(false);
const originalPolicyName: string = isNewPolicy ? '' : policyName!;
const isAllowedByLicense = license.canUseSearchableSnapshot();
+ const isCloudEnabled = Boolean(cloud?.isCloudEnabled);
- const { form } = useForm({
- schema,
- defaultValue: {
+ const serializer = useMemo(() => {
+ return createSerializer(isNewPolicy ? undefined : currentPolicy);
+ }, [isNewPolicy, currentPolicy]);
+
+ const deserializer = useMemo(() => {
+ return createDeserializer(isCloudEnabled);
+ }, [isCloudEnabled]);
+
+ const defaultValue = useMemo(
+ () => ({
...currentPolicy,
name: originalPolicyName,
- },
+ }),
+ [currentPolicy, originalPolicyName]
+ );
+
+ const schema = useMemo(() => {
+ return getSchema(isCloudEnabled);
+ }, [isCloudEnabled]);
+
+ const { form } = useForm({
+ schema,
+ defaultValue,
deserializer,
serializer,
});
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/form.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/form.tsx
index be8243cab289f..5d1add85bf9f4 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/form.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/form.tsx
@@ -9,20 +9,25 @@ import React, { FunctionComponent } from 'react';
import { Form as LibForm, FormHook } from '../../../../../shared_imports';
-import { ConfigurationIssuesProvider } from '../configuration_issues_context';
+import { ConfigurationProvider } from '../configuration_context';
import { FormErrorsProvider } from '../form_errors_context';
import { PhaseTimingsProvider } from '../phase_timings_context';
+import { GlobalFieldsProvider } from '../global_fields_context';
interface Props {
form: FormHook;
}
-export const Form: FunctionComponent = ({ form, children }) => (
-
-
-
- {children}
-
-
-
-);
+export const Form: FunctionComponent = ({ form, children }) => {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_context.tsx
similarity index 66%
rename from x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx
rename to x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_context.tsx
index c2e55f7aa6e61..97952a3a212c7 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_context.tsx
@@ -12,7 +12,7 @@ import { useFormData } from '../../../../shared_imports';
import { isUsingDefaultRolloverPath, isUsingCustomRolloverPath } from '../constants';
-export interface ConfigurationIssues {
+export interface Configuration {
/**
* Whether the serialized policy will use rollover. This blocks certain actions in
* the form such as hot phase (forcemerge, shrink) and cold phase (searchable snapshot).
@@ -28,7 +28,7 @@ export interface ConfigurationIssues {
isUsingSearchableSnapshotInColdPhase: boolean;
}
-const ConfigurationIssuesContext = createContext(null as any);
+const ConfigurationContext = createContext(null as any);
const pathToHotPhaseSearchableSnapshot =
'phases.hot.actions.searchable_snapshot.snapshot_repository';
@@ -36,7 +36,7 @@ const pathToHotPhaseSearchableSnapshot =
const pathToColdPhaseSearchableSnapshot =
'phases.cold.actions.searchable_snapshot.snapshot_repository';
-export const ConfigurationIssuesProvider: FunctionComponent = ({ children }) => {
+export const ConfigurationProvider: FunctionComponent = ({ children }) => {
const [formData] = useFormData({
watch: [
pathToHotPhaseSearchableSnapshot,
@@ -49,25 +49,18 @@ export const ConfigurationIssuesProvider: FunctionComponent = ({ children }) =>
// Provide default value, as path may become undefined if removed from the DOM
const isUsingCustomRollover = get(formData, isUsingCustomRolloverPath, true);
- return (
-
- {children}
-
- );
+ const context: Configuration = {
+ isUsingRollover: isUsingDefaultRollover === false ? isUsingCustomRollover : true,
+ isUsingSearchableSnapshotInHotPhase: get(formData, pathToHotPhaseSearchableSnapshot) != null,
+ isUsingSearchableSnapshotInColdPhase: get(formData, pathToColdPhaseSearchableSnapshot) != null,
+ };
+
+ return {children} ;
};
-export const useConfigurationIssues = () => {
- const ctx = useContext(ConfigurationIssuesContext);
- if (!ctx)
- throw new Error('Cannot use configuration issues outside of configuration issues context');
+export const useConfiguration = () => {
+ const ctx = useContext(ConfigurationContext);
+ if (!ctx) throw new Error('Cannot use configuration outside of configuration context');
return ctx;
};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts
index 227f135ca7b72..d8cffb974dfd1 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts
@@ -8,18 +8,29 @@
import { produce } from 'immer';
import { SerializedPolicy } from '../../../../../common/types';
-
import { splitSizeAndUnits } from '../../../lib/policies';
-
import { determineDataTierAllocationType, isUsingDefaultRollover } from '../../../lib';
-
+import { getDefaultRepository } from '../lib';
import { FormInternal } from '../types';
+import { CLOUD_DEFAULT_REPO } from '../constants';
-export const deserializer = (policy: SerializedPolicy): FormInternal => {
+export const createDeserializer = (isCloudEnabled: boolean) => (
+ policy: SerializedPolicy
+): FormInternal => {
const {
phases: { hot, warm, cold, frozen, delete: deletePhase },
} = policy;
+ let defaultRepository = getDefaultRepository([
+ hot?.actions.searchable_snapshot,
+ cold?.actions.searchable_snapshot,
+ frozen?.actions.searchable_snapshot,
+ ]);
+
+ if (!defaultRepository && isCloudEnabled) {
+ defaultRepository = CLOUD_DEFAULT_REPO;
+ }
+
const _meta: FormInternal['_meta'] = {
hot: {
isUsingDefaultRollover: isUsingDefaultRollover(policy),
@@ -49,6 +60,9 @@ export const deserializer = (policy: SerializedPolicy): FormInternal => {
delete: {
enabled: Boolean(deletePhase),
},
+ searchableSnapshot: {
+ repository: defaultRepository,
+ },
};
return produce(
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts
index ab60a631dacc5..bdb915ba62d44 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts
@@ -9,7 +9,7 @@ import { setAutoFreeze } from 'immer';
import { cloneDeep } from 'lodash';
import { SerializedPolicy } from '../../../../../common/types';
import { defaultRolloverAction } from '../../../constants';
-import { deserializer } from './deserializer';
+import { createDeserializer } from './deserializer';
import { createSerializer } from './serializer';
import { FormInternal } from '../types';
@@ -18,6 +18,8 @@ const isObject = (v: unknown): v is { [key: string]: any } =>
const unknownValue = { some: 'value' };
+const deserializer = createDeserializer(false);
+
const populateWithUnknownEntries = (v: unknown) => {
if (isObject(v)) {
for (const key of Object.keys(v)) {
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/global_fields_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/global_fields_context.tsx
new file mode 100644
index 0000000000000..30a00390a18cc
--- /dev/null
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/global_fields_context.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { createContext, FunctionComponent, useContext } from 'react';
+import { UseMultiFields, FieldHook, FieldConfig } from '../../../../shared_imports';
+
+/**
+ * Those are the fields that we always want present in our form.
+ */
+interface GlobalFieldsTypes {
+ deleteEnabled: boolean;
+ searchableSnapshotRepo: string;
+}
+
+type GlobalFields = {
+ [K in keyof GlobalFieldsTypes]: FieldHook;
+};
+
+const GlobalFieldsContext = createContext(null);
+
+export const globalFields: Record<
+ keyof GlobalFields,
+ { path: string; config?: FieldConfig }
+> = {
+ deleteEnabled: {
+ path: '_meta.delete.enabled',
+ },
+ searchableSnapshotRepo: {
+ path: '_meta.searchableSnapshot.repository',
+ },
+};
+
+export const GlobalFieldsProvider: FunctionComponent = ({ children }) => {
+ return (
+ fields={globalFields}>
+ {(fields) => {
+ return (
+ {children}
+ );
+ }}
+
+ );
+};
+
+export const useGlobalFields = () => {
+ const ctx = useContext(GlobalFieldsContext);
+ if (!ctx) throw new Error('Cannot use global fields outside of global fields context');
+
+ return ctx;
+};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts
index 6deb4d7fd4711..f31fedfac6681 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts
@@ -5,20 +5,17 @@
* 2.0.
*/
-export { deserializer } from './deserializer';
+export { createDeserializer } from './deserializer';
export { createSerializer } from './serializer';
-export { schema, searchableSnapshotFields } from './schema';
+export { getSchema } from './schema';
export * from './validations';
export { Form, EnhancedUseField as UseField } from './components';
-export {
- ConfigurationIssuesProvider,
- useConfigurationIssues,
-} from './configuration_issues_context';
+export { ConfigurationProvider, useConfiguration } from './configuration_context';
export { FormErrorsProvider, useFormErrorsContext } from './form_errors_context';
@@ -27,3 +24,5 @@ export {
usePhaseTimings,
PhaseTimingConfiguration,
} from './phase_timings_context';
+
+export { useGlobalFields, globalFields } from './global_fields_context';
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx
index 98ffb7e2dd7af..0cbee8832c55b 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx
@@ -8,7 +8,7 @@
import React, { createContext, FunctionComponent, useContext } from 'react';
import { useFormData } from '../../../../shared_imports';
import { FormInternal } from '../types';
-import { UseField } from './index';
+import { useGlobalFields } from './index';
export interface PhaseTimingConfiguration {
/**
@@ -48,6 +48,7 @@ export interface PhaseTimings {
const PhaseTimingsContext = createContext(null as any);
export const PhaseTimingsProvider: FunctionComponent = ({ children }) => {
+ const { deleteEnabled } = useGlobalFields();
const [formData] = useFormData({
watch: [
'_meta.warm.enabled',
@@ -58,21 +59,15 @@ export const PhaseTimingsProvider: FunctionComponent = ({ children }) => {
});
return (
-
- {(field) => {
- return (
-
- {children}
-
- );
+
+ >
+ {children}
+
);
};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts
index 5861c7b320de1..c0e489042586c 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts
@@ -9,12 +9,8 @@ import { i18n } from '@kbn/i18n';
import { FormSchema, fieldValidators } from '../../../../shared_imports';
import { defaultIndexPriority } from '../../../constants';
-import { ROLLOVER_FORM_PATHS } from '../constants';
-
-import { FormInternal } from '../types';
-
-const rolloverFormPaths = Object.values(ROLLOVER_FORM_PATHS);
-
+import { ROLLOVER_FORM_PATHS, CLOUD_DEFAULT_REPO } from '../constants';
+import { i18nTexts } from '../i18n_texts';
import {
ifExistsNumberGreaterThanZero,
ifExistsNumberNonNegative,
@@ -22,7 +18,7 @@ import {
minAgeValidator,
} from './validations';
-import { i18nTexts } from '../i18n_texts';
+const rolloverFormPaths = Object.values(ROLLOVER_FORM_PATHS);
const { emptyField, numberGreaterThanField } = fieldValidators;
@@ -54,6 +50,13 @@ export const searchableSnapshotFields = {
validations: [
{ validator: emptyField(i18nTexts.editPolicy.errors.searchableSnapshotRepoRequired) },
],
+ // TODO: update text copy
+ helpText: i18n.translate(
+ 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshot.repositoryHelpText',
+ {
+ defaultMessage: 'Each phase uses the same snapshot repository.',
+ }
+ ),
},
storage: {
label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.searchableSnapshot.storageLabel', {
@@ -114,7 +117,7 @@ const getPriorityField = (phase: 'hot' | 'warm' | 'cold' | 'frozen') => ({
serializer: serializers.stringToNumber,
});
-export const schema: FormSchema = {
+export const getSchema = (isCloudEnabled: boolean): FormSchema => ({
_meta: {
hot: {
isUsingDefaultRollover: {
@@ -230,6 +233,11 @@ export const schema: FormSchema = {
defaultValue: 'd',
},
},
+ searchableSnapshot: {
+ repository: {
+ defaultValue: isCloudEnabled ? CLOUD_DEFAULT_REPO : '',
+ },
+ },
},
phases: {
hot: {
@@ -288,6 +296,7 @@ export const schema: FormSchema = {
set_priority: {
priority: getPriorityField('hot'),
},
+ searchable_snapshot: searchableSnapshotFields,
},
},
warm: {
@@ -375,4 +384,4 @@ export const schema: FormSchema = {
},
},
},
-};
+});
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts
index b21545ce1739c..57112b0e1cb16 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts
@@ -124,7 +124,12 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
/**
* HOT PHASE SEARCHABLE SNAPSHOT
*/
- if (!updatedPolicy.phases.hot!.actions?.searchable_snapshot) {
+ if (updatedPolicy.phases.hot!.actions?.searchable_snapshot) {
+ hotPhaseActions.searchable_snapshot = {
+ ...hotPhaseActions.searchable_snapshot,
+ snapshot_repository: _meta.searchableSnapshot.repository,
+ };
+ } else {
delete hotPhaseActions.searchable_snapshot;
}
}
@@ -234,7 +239,12 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
/**
* COLD PHASE SEARCHABLE SNAPSHOT
*/
- if (!updatedPolicy.phases.cold?.actions?.searchable_snapshot) {
+ if (updatedPolicy.phases.cold?.actions?.searchable_snapshot) {
+ coldPhase.actions.searchable_snapshot = {
+ ...coldPhase.actions.searchable_snapshot,
+ snapshot_repository: _meta.searchableSnapshot.repository,
+ };
+ } else {
delete coldPhase.actions.searchable_snapshot;
}
} else {
@@ -251,7 +261,12 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
/**
* FROZEN PHASE SEARCHABLE SNAPSHOT
*/
- if (!updatedPolicy.phases.frozen?.actions?.searchable_snapshot) {
+ if (updatedPolicy.phases.frozen?.actions?.searchable_snapshot) {
+ frozenPhase.actions.searchable_snapshot = {
+ ...frozenPhase.actions.searchable_snapshot,
+ snapshot_repository: _meta.searchableSnapshot.repository,
+ };
+ } else {
delete frozenPhase.actions.searchable_snapshot;
}
} else {
@@ -271,7 +286,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
deletePhase.actions.delete = deletePhase.actions.delete ?? {};
/**
- * DELETE PHASE SEARCHABLE SNAPSHOT
+ * DELETE PHASE MIN AGE
*/
if (updatedPolicy.phases.delete?.min_age) {
deletePhase.min_age = `${updatedPolicy.phases.delete!.min_age}${_meta.delete.minAgeUnit}`;
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts
index 8a9635e2db219..d4a26924385f0 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.test.ts
@@ -6,13 +6,15 @@
*/
import { flow } from 'fp-ts/function';
-import { deserializer } from '../form';
+import { createDeserializer } from '../form';
import {
formDataToAbsoluteTimings,
calculateRelativeFromAbsoluteMilliseconds,
} from './absolute_timing_to_relative_timing';
+const deserializer = createDeserializer(false);
+
export const calculateRelativeTimingMs = flow(
formDataToAbsoluteTimings,
calculateRelativeFromAbsoluteMilliseconds
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/get_default_repository.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/get_default_repository.ts
new file mode 100644
index 0000000000000..43e911333e357
--- /dev/null
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/get_default_repository.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 { SearchableSnapshotAction } from '../../../../../common/types';
+
+export const getDefaultRepository = (
+ configs: Array
+): string => {
+ if (configs.length === 0) {
+ return '';
+ }
+ if (Boolean(configs[0]?.snapshot_repository)) {
+ return configs[0]!.snapshot_repository;
+ }
+ return getDefaultRepository(configs.slice(1));
+};
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts
index af4757a7b7105..19d87532f2bfe 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/index.ts
@@ -12,3 +12,5 @@ export {
PhaseAgeInMilliseconds,
RelativePhaseTimingInMs,
} from './absolute_timing_to_relative_timing';
+
+export { getDefaultRepository } from './get_default_repository';
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts
index 4330cde378b6d..977554f12da42 100644
--- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts
@@ -4,7 +4,6 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-
import { SerializedPolicy } from '../../../../common/types';
export type DataTierAllocationType = 'node_roles' | 'node_attrs' | 'none';
@@ -76,5 +75,8 @@ export interface FormInternal extends SerializedPolicy {
cold: ColdPhaseMetaFields;
frozen: FrozenPhaseMetaFields;
delete: DeletePhaseMetaFields;
+ searchableSnapshot: {
+ repository: string;
+ };
};
}
diff --git a/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts b/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts
index cf2d5d5efc0f8..a8e0182ada77b 100644
--- a/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts
+++ b/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts
@@ -23,6 +23,7 @@ export {
useFormContext,
FormSchema,
ValidationConfig,
+ UseMultiFields,
} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
export { fieldValidators } from '../../../../src/plugins/es_ui_shared/static/forms/helpers';
From c937f2648ecbc869042bf3d66f3178db03704089 Mon Sep 17 00:00:00 2001
From: Devon Thomson
Date: Tue, 16 Mar 2021 11:23:19 -0400
Subject: [PATCH 17/24] tiny fix for loading state in dashboard top nav
(#94643)
---
.../dashboard/public/application/top_nav/dashboard_top_nav.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx
index 6230a16f10491..a82aa78b815ec 100644
--- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx
+++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx
@@ -249,11 +249,11 @@ export function DashboardTopNav({
useReplace: true,
});
} else {
- setIsSaveInProgress(false);
dashboardStateManager.resetState();
chrome.docTitle.change(dashboardStateManager.savedDashboard.lastSavedTitle);
}
}
+ setIsSaveInProgress(false);
return { id };
})
.catch((error) => {
From badf38b0cd5ae78bff3bb8b730253fd617a3f3be Mon Sep 17 00:00:00 2001
From: Constance
Date: Tue, 16 Mar 2021 08:30:32 -0700
Subject: [PATCH 18/24] [Curation] Add support for a custom drag handle to the
Result component (#94652)
* Update Result component to render a custom drag handle
* Update Result library with a draggable example
- note: this doesn't actually handle reorder logic, it's purely a UI/UX example
* Update CurationResult to pass dragHandleProps through to Result
---
.../curation/results/curation_result.test.tsx | 9 +++++--
.../curation/results/curation_result.tsx | 5 +++-
.../app_search/components/library/library.tsx | 25 +++++++++++++++++++
.../app_search/components/result/result.scss | 17 ++++++++++---
.../components/result/result.test.tsx | 15 +++++++++++
.../app_search/components/result/result.tsx | 8 ++++++
6 files changed, 72 insertions(+), 7 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.test.tsx
index 5c417d308636e..460c0f4dfa44c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.test.tsx
@@ -8,6 +8,7 @@
import { setMockValues } from '../../../../../__mocks__';
import React from 'react';
+import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd';
import { shallow, ShallowWrapper } from 'enzyme';
@@ -29,12 +30,15 @@ describe('CurationResult', () => {
{ title: 'add', iconType: 'plus', onClick: () => {} },
{ title: 'remove', iconType: 'minus', onClick: () => {} },
];
+ const mockDragging = {} as DraggableProvidedDragHandleProps; // Passed from EuiDraggable
let wrapper: ShallowWrapper;
beforeAll(() => {
setMockValues(values);
- wrapper = shallow( );
+ wrapper = shallow(
+
+ );
});
it('passes EngineLogic state', () => {
@@ -42,8 +46,9 @@ describe('CurationResult', () => {
expect(wrapper.find(Result).prop('schemaForTypeHighlights')).toEqual('some mock schema');
});
- it('passes result and actions props', () => {
+ it('passes result, actions, and dragHandleProps props', () => {
expect(wrapper.find(Result).prop('result')).toEqual(mockResult);
expect(wrapper.find(Result).prop('actions')).toEqual(mockActions);
+ expect(wrapper.find(Result).prop('dragHandleProps')).toEqual(mockDragging);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.tsx
index 3be11bcd65956..c737d93ce1823 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/curation_result.tsx
@@ -6,6 +6,7 @@
*/
import React from 'react';
+import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd';
import { useValues } from 'kea';
@@ -18,9 +19,10 @@ import { Result as ResultType, ResultAction } from '../../../result/types';
interface Props {
result: ResultType;
actions: ResultAction[];
+ dragHandleProps?: DraggableProvidedDragHandleProps;
}
-export const CurationResult: React.FC = ({ result, actions }) => {
+export const CurationResult: React.FC = ({ result, actions, dragHandleProps }) => {
const {
isMetaEngine,
engine: { schema },
@@ -33,6 +35,7 @@ export const CurationResult: React.FC = ({ result, actions }) => {
actions={actions}
isMetaEngine={isMetaEngine}
schemaForTypeHighlights={schema}
+ dragHandleProps={dragHandleProps}
/>
>
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
index 3f72199d12805..594584d9ba101 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx
@@ -14,6 +14,9 @@ import {
EuiTitle,
EuiPageContentBody,
EuiPageContent,
+ EuiDragDropContext,
+ EuiDroppable,
+ EuiDraggable,
} from '@elastic/eui';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
@@ -228,6 +231,28 @@ export const Library: React.FC = () => {
+
+
+ With a drag handle
+
+
+ {}}>
+
+ {[1, 2, 3].map((_, i) => (
+
+ {(provided) => }
+
+ ))}
+
+
+
+
With field value type highlights
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
index f69acbdaba150..5f1b165f2c362 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss
@@ -1,10 +1,10 @@
.appSearchResult {
display: grid;
- grid-template-columns: 1fr auto;
- grid-template-rows: 1fr auto;
+ grid-template-columns: auto 1fr auto;
+ grid-template-rows: auto 1fr auto;
grid-template-areas:
- 'content actions'
- 'toggle actions';
+ 'drag content actions'
+ 'drag toggle actions';
overflow: hidden; // Prevents child background-colors from clipping outside of panel border-radius
&__content {
@@ -52,6 +52,15 @@
background-color: $euiPageBackgroundColor;
}
}
+
+ &__dragHandle {
+ grid-area: drag;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: $euiSizeXL;
+ border-right: $euiBorderThin;
+ }
}
/**
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
index 86b71229f3785..15c9ee2967d3e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx
@@ -6,6 +6,7 @@
*/
import React from 'react';
+import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd';
import { shallow, ShallowWrapper } from 'enzyme';
@@ -129,6 +130,20 @@ describe('Result', () => {
});
});
+ describe('dragging', () => {
+ // In the real world, the drag library sets data attributes, role, tabIndex, etc.
+ const mockDragHandleProps = ({
+ someMockProp: true,
+ } as unknown) as DraggableProvidedDragHandleProps;
+
+ it('will render a drag handle with the passed props', () => {
+ const wrapper = shallow( );
+
+ expect(wrapper.find('.appSearchResult__dragHandle')).toHaveLength(1);
+ expect(wrapper.find('.appSearchResult__dragHandle').prop('someMockProp')).toEqual(true);
+ });
+ });
+
it('will render field details with type highlights if schemaForTypeHighlights has been provided', () => {
const wrapper = shallow(
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
index 2812b596e87fa..89208a041af35 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx
@@ -6,6 +6,7 @@
*/
import React, { useState, useMemo } from 'react';
+import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd';
import classNames from 'classnames';
@@ -31,6 +32,7 @@ interface Props {
shouldLinkToDetailPage?: boolean;
schemaForTypeHighlights?: Schema;
actions?: ResultAction[];
+ dragHandleProps?: DraggableProvidedDragHandleProps;
}
const RESULT_CUTOFF = 5;
@@ -42,6 +44,7 @@ export const Result: React.FC = ({
shouldLinkToDetailPage = false,
schemaForTypeHighlights,
actions = [],
+ dragHandleProps,
}) => {
const [isOpen, setIsOpen] = useState(false);
@@ -87,6 +90,11 @@ export const Result: React.FC = ({
values: { id: result[ID].raw },
})}
>
+ {dragHandleProps && (
+
+
+
+ )}
{conditionallyLinkedArticle(
<>
Date: Tue, 16 Mar 2021 16:41:07 +0100
Subject: [PATCH 19/24] [Uptime] Synthetic check steps list view (#90978)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../translations/translations/ja-JP.json | 6 -
.../translations/translations/zh-CN.json | 6 -
x-pack/plugins/uptime/common/constants/ui.ts | 2 +
.../uptime/common/runtime_types/ping/ping.ts | 2 +
.../components/common/step_detail_link.tsx | 12 +-
.../columns/ping_timestamp/nav_buttons.tsx | 8 +-
.../ping_timestamp/ping_timestamp.test.tsx | 12 +-
.../columns/ping_timestamp/ping_timestamp.tsx | 45 ++-
.../step_image_caption.test.tsx | 5 +-
.../ping_timestamp/step_image_caption.tsx | 33 ++-
.../ping_timestamp/step_image_popover.tsx | 3 +-
.../monitor/ping_list/expanded_row.test.tsx | 37 ++-
.../monitor/ping_list/expanded_row.tsx | 19 --
.../monitor/ping_list/ping_list.tsx | 57 +++-
.../synthetics/browser_expanded_row.test.tsx | 203 --------------
.../synthetics/browser_expanded_row.tsx | 65 -----
.../synthetics/executed_journey.test.tsx | 265 ------------------
.../monitor/synthetics/executed_journey.tsx | 88 ------
.../monitor/synthetics/executed_step.tsx | 110 --------
.../monitor/synthetics/status_badge.test.tsx | 42 ---
.../step_detail/step_detail_container.tsx | 2 +-
.../step_detail/use_monitor_breadcrumb.tsx | 33 ++-
.../use_monitor_breadcrumbs.test.tsx | 84 +++++-
.../columns/monitor_status_column.tsx | 2 +-
.../step_expanded_row/screenshot_link.tsx | 47 ++++
.../step_expanded_row/step_screenshots.tsx | 86 ++++++
.../synthetics/check_steps/step_image.tsx | 28 ++
.../synthetics/check_steps/step_list.test.tsx | 144 ++++++++++
.../synthetics/check_steps/steps_list.tsx | 175 ++++++++++++
.../synthetics/check_steps/use_check_steps.ts | 29 ++
.../check_steps/use_expanded_row.test.tsx | 152 ++++++++++
.../check_steps/use_expanded_row.tsx | 88 ++++++
.../synthetics/code_block_accordion.tsx | 4 +-
.../synthetics/console_event.test.tsx | 0
.../synthetics/console_event.tsx | 4 +-
.../console_output_event_list.test.tsx | 0
.../synthetics/console_output_event_list.tsx | 4 +-
.../synthetics/empty_journey.test.tsx | 0
.../synthetics/empty_journey.tsx | 0
.../synthetics/executed_step.test.tsx | 53 ++--
.../components/synthetics/executed_step.tsx | 118 ++++++++
.../synthetics/status_badge.test.tsx | 33 +++
.../{monitor => }/synthetics/status_badge.tsx | 20 +-
.../step_screenshot_display.test.tsx | 2 +-
.../synthetics/step_screenshot_display.tsx | 89 ++----
.../components/synthetics/translations.ts | 20 ++
.../uptime/public/hooks/use_telemetry.ts | 1 +
x-pack/plugins/uptime/public/pages/index.ts | 2 +-
.../pages/synthetics/checks_navigation.tsx | 60 ++++
.../{ => synthetics}/step_detail_page.tsx | 6 +-
.../pages/synthetics/synthetics_checks.tsx | 44 +++
x-pack/plugins/uptime/public/routes.tsx | 9 +
.../uptime/public/state/api/journey.ts | 17 ++
.../lib/requests/get_journey_details.ts | 6 +-
.../lib/requests/get_journey_screenshot.ts | 4 +-
.../lib/requests/get_journey_steps.test.ts | 4 +-
.../server/lib/requests/get_journey_steps.ts | 2 +-
.../lib/requests/get_last_successful_step.ts | 77 +++++
.../uptime/server/lib/requests/index.ts | 2 +
.../plugins/uptime/server/rest_api/index.ts | 2 +
.../rest_api/pings/journey_screenshots.ts | 5 +-
.../uptime/server/rest_api/pings/journeys.ts | 23 +-
.../synthetics/last_successful_step.ts | 33 +++
63 files changed, 1514 insertions(+), 1020 deletions(-)
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/code_block_accordion.tsx (87%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_event.test.tsx (100%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_event.tsx (89%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_output_event_list.test.tsx (100%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_output_event_list.tsx (92%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/empty_journey.test.tsx (100%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/empty_journey.tsx (100%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/executed_step.test.tsx (54%)
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/status_badge.tsx (66%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/step_screenshot_display.test.tsx (96%)
rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/step_screenshot_display.tsx (52%)
create mode 100644 x-pack/plugins/uptime/public/components/synthetics/translations.ts
create mode 100644 x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx
rename x-pack/plugins/uptime/public/pages/{ => synthetics}/step_detail_page.tsx (75%)
create mode 100644 x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx
create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts
create mode 100644 x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 32b749d2d7fa7..a558834ab67f6 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -23257,12 +23257,8 @@
"xpack.uptime.synthetics.emptyJourney.message.footer": "表示する詳細情報はありません。",
"xpack.uptime.synthetics.emptyJourney.message.heading": "ステップが含まれていませんでした。",
"xpack.uptime.synthetics.emptyJourney.title": "ステップがありません。",
- "xpack.uptime.synthetics.executedJourney.heading": "概要情報",
"xpack.uptime.synthetics.executedStep.errorHeading": "エラー",
- "xpack.uptime.synthetics.executedStep.scriptHeading": "スクリプトのステップ",
"xpack.uptime.synthetics.executedStep.stackTrace": "スタックトレース",
- "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}. {stepName}",
- "xpack.uptime.synthetics.experimentalCallout.title": "実験的機能",
"xpack.uptime.synthetics.imageLoadingSpinner.ariaLabel": "画像を示すアニメーションスピナーを読み込んでいます",
"xpack.uptime.synthetics.journey.allFailedMessage": "{total}ステップ - すべて失敗またはスキップされました",
"xpack.uptime.synthetics.journey.allSucceededMessage": "{total}ステップ - すべて成功しました",
@@ -23273,8 +23269,6 @@
"xpack.uptime.synthetics.screenshot.noImageMessage": "画像がありません",
"xpack.uptime.synthetics.screenshotDisplay.altText": "名前「{stepName}」のステップのスクリーンショット",
"xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "スクリーンショット",
- "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名前「{stepName}」のステップのサムネイルスクリーンショット",
- "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "サムネイルスクリーンショット",
"xpack.uptime.synthetics.statusBadge.failedMessage": "失敗",
"xpack.uptime.synthetics.statusBadge.skippedMessage": "スキップ",
"xpack.uptime.synthetics.statusBadge.succeededMessage": "成功",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index db3ca3d56ec5a..9113b44d6ad31 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -23614,12 +23614,8 @@
"xpack.uptime.synthetics.emptyJourney.message.footer": "没有更多可显示的信息。",
"xpack.uptime.synthetics.emptyJourney.message.heading": "此过程不包含任何步骤。",
"xpack.uptime.synthetics.emptyJourney.title": "没有此过程的任何步骤",
- "xpack.uptime.synthetics.executedJourney.heading": "摘要信息",
"xpack.uptime.synthetics.executedStep.errorHeading": "错误",
- "xpack.uptime.synthetics.executedStep.scriptHeading": "步骤脚本",
"xpack.uptime.synthetics.executedStep.stackTrace": "堆栈跟踪",
- "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}:{stepName}",
- "xpack.uptime.synthetics.experimentalCallout.title": "实验功能",
"xpack.uptime.synthetics.imageLoadingSpinner.ariaLabel": "表示图像正在加载的动画旋转图标",
"xpack.uptime.synthetics.journey.allFailedMessage": "{total} 个步骤 - 全部失败或跳过",
"xpack.uptime.synthetics.journey.allSucceededMessage": "{total} 个步骤 - 全部成功",
@@ -23630,8 +23626,6 @@
"xpack.uptime.synthetics.screenshot.noImageMessage": "没有可用图像",
"xpack.uptime.synthetics.screenshotDisplay.altText": "名称为“{stepName}”的步骤的屏幕截图",
"xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "屏幕截图",
- "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名称为“{stepName}”的步骤的缩略屏幕截图",
- "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "缩略屏幕截图",
"xpack.uptime.synthetics.statusBadge.failedMessage": "失败",
"xpack.uptime.synthetics.statusBadge.skippedMessage": "已跳过",
"xpack.uptime.synthetics.statusBadge.succeededMessage": "成功",
diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts
index 880bc0f92ddf6..dcaf4bb310ad7 100644
--- a/x-pack/plugins/uptime/common/constants/ui.ts
+++ b/x-pack/plugins/uptime/common/constants/ui.ts
@@ -15,6 +15,8 @@ export const CERTIFICATES_ROUTE = '/certificates';
export const STEP_DETAIL_ROUTE = '/journey/:checkGroupId/step/:stepIndex';
+export const SYNTHETIC_CHECK_STEPS_ROUTE = '/journey/:checkGroupId/steps';
+
export enum STATUS {
UP = 'up',
DOWN = 'down',
diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts
index 8991d52f6a920..77b9473f2912e 100644
--- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts
+++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts
@@ -216,6 +216,7 @@ export const PingType = t.intersection([
type: t.string,
url: t.string,
end: t.number,
+ text: t.string,
}),
}),
tags: t.array(t.string),
@@ -251,6 +252,7 @@ export const SyntheticsJourneyApiResponseType = t.intersection([
t.intersection([
t.type({
timestamp: t.string,
+ journey: PingType,
}),
t.partial({
next: t.type({
diff --git a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
index 313dd18e67c11..fa6d0b4c3f8bb 100644
--- a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
+++ b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx
@@ -6,7 +6,7 @@
*/
import React, { FC } from 'react';
-import { ReactRouterEuiButton } from './react_router_helpers';
+import { ReactRouterEuiButtonEmpty } from './react_router_helpers';
interface StepDetailLinkProps {
/**
@@ -23,14 +23,8 @@ export const StepDetailLink: FC = ({ children, checkGroupId
const to = `/journey/${checkGroupId}/step/${stepIndex}`;
return (
-
+
{children}
-
+
);
};
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx
index 390a133b1819b..3b0aad721be8a 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx
@@ -6,7 +6,7 @@
*/
import { EuiButtonIcon, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
-import React from 'react';
+import React, { MouseEvent } from 'react';
import { nextAriaLabel, prevAriaLabel } from './translations';
export interface NavButtonsProps {
@@ -34,8 +34,9 @@ export const NavButtons: React.FC = ({
disabled={stepNumber === 1}
color="subdued"
size="s"
- onClick={() => {
+ onClick={(evt: MouseEvent) => {
setStepNumber(stepNumber - 1);
+ evt.stopPropagation();
}}
iconType="arrowLeft"
aria-label={prevAriaLabel}
@@ -46,8 +47,9 @@ export const NavButtons: React.FC = ({
disabled={stepNumber === maxSteps}
color="subdued"
size="s"
- onClick={() => {
+ onClick={(evt: MouseEvent) => {
setStepNumber(stepNumber + 1);
+ evt.stopPropagation();
}}
iconType="arrowRight"
aria-label={nextAriaLabel}
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx
index 2a1989cafa434..d628b2d8388f9 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx
@@ -12,6 +12,8 @@ import { mockReduxHooks } from '../../../../../lib/helper/test_helpers';
import { render } from '../../../../../lib/helper/rtl_helpers';
import { Ping } from '../../../../../../common/runtime_types/ping';
import * as observabilityPublic from '../../../../../../../observability/public';
+import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column';
+import moment from 'moment';
mockReduxHooks();
@@ -68,7 +70,7 @@ describe('Ping Timestamp component', () => {
.spyOn(observabilityPublic, 'useFetcher')
.mockReturnValue({ status: fetchStatus, data: null, refetch: () => null });
const { getByTestId } = render(
-
+
);
expect(getByTestId('pingTimestampSpinner')).toBeInTheDocument();
}
@@ -79,7 +81,7 @@ describe('Ping Timestamp component', () => {
.spyOn(observabilityPublic, 'useFetcher')
.mockReturnValue({ status: FETCH_STATUS.SUCCESS, data: null, refetch: () => null });
const { getByTestId } = render(
-
+
);
expect(getByTestId('pingTimestampNoImageAvailable')).toBeInTheDocument();
});
@@ -91,7 +93,9 @@ describe('Ping Timestamp component', () => {
data: { src },
refetch: () => null,
});
- const { container } = render( );
+ const { container } = render(
+
+ );
expect(container.querySelector('img')?.src).toBe(src);
});
@@ -103,7 +107,7 @@ describe('Ping Timestamp component', () => {
refetch: () => null,
});
const { getByAltText, getAllByText, queryByAltText } = render(
-
+
);
const caption = getAllByText('Nov 26, 2020 10:28:56 AM');
fireEvent.mouseEnter(caption[0]);
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx
index cfb92dd31190e..16553e9de8604 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx
@@ -8,18 +8,15 @@
import React, { useContext, useEffect, useState } from 'react';
import useIntersection from 'react-use/lib/useIntersection';
import styled from 'styled-components';
-import moment from 'moment';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Ping } from '../../../../../../common/runtime_types/ping';
import { useFetcher, FETCH_STATUS } from '../../../../../../../observability/public';
import { getJourneyScreenshot } from '../../../../../state/api/journey';
import { UptimeSettingsContext } from '../../../../../contexts';
-import { NavButtons } from './nav_buttons';
import { NoImageDisplay } from './no_image_display';
import { StepImageCaption } from './step_image_caption';
import { StepImagePopover } from './step_image_popover';
import { formatCaptionContent } from './translations';
-import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column';
const StepDiv = styled.div`
figure.euiImage {
@@ -27,25 +24,16 @@ const StepDiv = styled.div`
display: none;
}
}
-
- position: relative;
- div.stepArrows {
- display: none;
- }
- :hover {
- div.stepArrows {
- display: flex;
- }
- }
`;
interface Props {
- timestamp: string;
+ label?: string;
ping: Ping;
+ initialStepNo?: number;
}
-export const PingTimestamp = ({ timestamp, ping }: Props) => {
- const [stepNumber, setStepNumber] = useState(1);
+export const PingTimestamp = ({ label, ping, initialStepNo = 1 }: Props) => {
+ const [stepNumber, setStepNumber] = useState(initialStepNo);
const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false);
const [stepImages, setStepImages] = useState([]);
@@ -77,6 +65,8 @@ export const PingTimestamp = ({ timestamp, ping }: Props) => {
const captionContent = formatCaptionContent(stepNumber, data?.maxSteps);
+ const [numberOfCaptions, setNumberOfCaptions] = useState(0);
+
const ImageCaption = (
{
maxSteps={data?.maxSteps}
setStepNumber={setStepNumber}
stepNumber={stepNumber}
- timestamp={timestamp}
isLoading={status === FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING}
+ label={label}
+ onVisible={(val) => setNumberOfCaptions((prevVal) => (val ? prevVal + 1 : prevVal - 1))}
/>
);
+ useEffect(() => {
+ // This is a hack to get state if image is in full screen, we should refactor
+ // it once eui image exposes it's full screen state
+ // we are checking if number of captions are 2, that means
+ // image is in full screen mode since caption is also rendered on
+ // full screen image
+ // we dont want to change image displayed in thumbnail
+ if (numberOfCaptions === 1 && stepNumber !== initialStepNo) {
+ setStepNumber(initialStepNo);
+ }
+ }, [numberOfCaptions, initialStepNo, stepNumber]);
+
return (
@@ -111,16 +114,10 @@ export const PingTimestamp = ({ timestamp, ping }: Props) => {
isPending={status === FETCH_STATUS.PENDING}
/>
)}
-
- {getShortTimeStamp(moment(timestamp))}
+ {label}
);
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx
index a33e587093279..5c2c4d3669e79 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx
@@ -9,6 +9,8 @@ import { fireEvent, waitFor } from '@testing-library/react';
import React from 'react';
import { render } from '../../../../../lib/helper/rtl_helpers';
import { StepImageCaption, StepImageCaptionProps } from './step_image_caption';
+import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column';
+import moment from 'moment';
describe('StepImageCaption', () => {
let defaultProps: StepImageCaptionProps;
@@ -20,7 +22,8 @@ describe('StepImageCaption', () => {
maxSteps: 3,
setStepNumber: jest.fn(),
stepNumber: 2,
- timestamp: '2020-11-26T15:28:56.896Z',
+ label: getShortTimeStamp(moment('2020-11-26T15:28:56.896Z')),
+ onVisible: jest.fn(),
isLoading: false,
};
});
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx
index fe9709a02b684..80d41ccc23dc8 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx
@@ -5,11 +5,9 @@
* 2.0.
*/
+import React, { MouseEvent, useEffect } from 'react';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
-import React from 'react';
-import moment from 'moment';
import { nextAriaLabel, prevAriaLabel } from './translations';
-import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column';
import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
export interface StepImageCaptionProps {
@@ -18,7 +16,8 @@ export interface StepImageCaptionProps {
maxSteps?: number;
setStepNumber: React.Dispatch>;
stepNumber: number;
- timestamp: string;
+ label?: string;
+ onVisible: (val: boolean) => void;
isLoading: boolean;
}
@@ -35,19 +34,34 @@ export const StepImageCaption: React.FC = ({
maxSteps,
setStepNumber,
stepNumber,
- timestamp,
isLoading,
+ label,
+ onVisible,
}) => {
+ useEffect(() => {
+ onVisible(true);
+ return () => {
+ onVisible(false);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
return (
-
+ {
+ // we don't want this to be captured by row click which leads to step list page
+ evt.stopPropagation();
+ }}
+ >
{imgSrc && (
{
+ onClick={(evt: MouseEvent) => {
setStepNumber(stepNumber - 1);
+ evt.preventDefault();
}}
iconType="arrowLeft"
aria-label={prevAriaLabel}
@@ -62,8 +76,9 @@ export const StepImageCaption: React.FC = ({
{
+ onClick={(evt: MouseEvent) => {
setStepNumber(stepNumber + 1);
+ evt.stopPropagation();
}}
iconType="arrowRight"
iconSide="right"
@@ -75,7 +90,7 @@ export const StepImageCaption: React.FC = ({
)}
- {getShortTimeStamp(moment(timestamp))}
+ {label}
);
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx
index 4fc8db515a5d6..d3dce3a2505b2 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx
@@ -38,7 +38,7 @@ export const StepImagePopover: React.FC = ({
isImagePopoverOpen,
}) => (
= ({
/>
}
isOpen={isImagePopoverOpen}
+ closePopover={() => {}}
>
{
>
-
-
-
+ color="primary"
+ >
+
+ The Title ",
+ "hash": "testhash",
+ }
+ }
+ />
+
+
+ ,
+ "title": "Response Body",
+ },
+ ]
+ }
+ />
+
`);
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx
index 2599b8ed9fdca..df0d273d3bc3a 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx
@@ -21,7 +21,6 @@ import { i18n } from '@kbn/i18n';
import { Ping, HttpResponseBody } from '../../../../common/runtime_types';
import { DocLinkForBody } from './doc_link_body';
import { PingRedirects } from './ping_redirects';
-import { BrowserExpandedRow } from '../synthetics/browser_expanded_row';
import { PingHeaders } from './headers';
interface Props {
@@ -57,24 +56,6 @@ const BodyExcerpt = ({ content }: { content: string }) =>
export const PingListExpandedRowComponent = ({ ping }: Props) => {
const listItems = [];
- if (ping.monitor.type === 'browser') {
- return (
-
-
-
-
-
-
-
-
- );
- }
-
// Show the error block
if (ping.error) {
listItems.push({
diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx
index 18bc5f5ec3ecb..65644ce493906 100644
--- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx
@@ -7,8 +7,10 @@
import { EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import React, { useCallback, useState, useEffect } from 'react';
+import React, { useCallback, useState, useEffect, MouseEvent } from 'react';
import styled from 'styled-components';
+import { useHistory } from 'react-router-dom';
+import moment from 'moment';
import { useDispatch } from 'react-redux';
import { Ping } from '../../../../common/runtime_types';
import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper';
@@ -27,6 +29,7 @@ import { FailedStep } from './columns/failed_step';
import { usePingsList } from './use_pings';
import { PingListHeader } from './ping_list_header';
import { clearPings } from '../../../state/actions';
+import { getShortTimeStamp } from '../../overview/monitor_list/columns/monitor_status_column';
export const SpanWithMargin = styled.span`
margin-right: 16px;
@@ -69,6 +72,8 @@ export const PingList = () => {
const dispatch = useDispatch();
+ const history = useHistory();
+
const pruneJourneysCallback = useCallback(
(checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)),
[dispatch]
@@ -140,7 +145,7 @@ export const PingList = () => {
field: 'timestamp',
name: TIMESTAMP_LABEL,
render: (timestamp: string, item: Ping) => (
-
+
),
},
]
@@ -197,20 +202,43 @@ export const PingList = () => {
},
]
: []),
- {
- align: 'right',
- width: '24px',
- isExpander: true,
- render: (item: Ping) => (
-
- ),
- },
+ ...(monitorType !== MONITOR_TYPES.BROWSER
+ ? [
+ {
+ align: 'right',
+ width: '24px',
+ isExpander: true,
+ render: (item: Ping) => (
+
+ ),
+ },
+ ]
+ : []),
];
+ const getRowProps = (item: Ping) => {
+ if (monitorType !== MONITOR_TYPES.BROWSER) {
+ return {};
+ }
+ const { monitor } = item;
+ return {
+ height: '85px',
+ 'data-test-subj': `row-${monitor.check_group}`,
+ onClick: (evt: MouseEvent) => {
+ const targetElem = evt.target as HTMLElement;
+
+ // we dont want to capture image click event
+ if (targetElem.tagName !== 'IMG' && targetElem.tagName !== 'path') {
+ history.push(`/journey/${monitor.check_group}/steps`);
+ }
+ },
+ };
+ };
+
const pagination: Pagination = {
initialPageSize: DEFAULT_PAGE_SIZE,
pageIndex,
@@ -247,6 +275,7 @@ export const PingList = () => {
setPageIndex(criteria.page!.index);
}}
tableLayout={'auto'}
+ rowProps={getRowProps}
/>
);
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx
deleted file mode 100644
index 396d51e3002b2..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx
+++ /dev/null
@@ -1,203 +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 { shallowWithIntl } from '@kbn/test/jest';
-import React from 'react';
-import { BrowserExpandedRowComponent } from './browser_expanded_row';
-import { Ping } from '../../../../common/runtime_types';
-
-describe('BrowserExpandedRowComponent', () => {
- let defStep: Ping;
- beforeEach(() => {
- defStep = {
- docId: 'doc-id',
- timestamp: '123',
- monitor: {
- duration: {
- us: 100,
- },
- id: 'mon-id',
- status: 'up',
- type: 'browser',
- },
- };
- });
-
- it('returns empty step state when no journey', () => {
- expect(shallowWithIntl( )).toMatchInlineSnapshot(
- ` `
- );
- });
-
- it('returns empty step state when journey has no steps', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(` `);
- });
-
- it('displays loading spinner when loading', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(`
-
-
-
- `);
- });
-
- it('renders executed journey when step/end is present', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(`
-
- `);
- });
-
- it('handles case where synth type is somehow missing', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(`""`);
- });
-
- it('renders console output step list when only console steps are present', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(`
-
- `);
- });
-
- it('renders null when only unsupported steps are present', () => {
- expect(
- shallowWithIntl(
-
- )
- ).toMatchInlineSnapshot(`""`);
- });
-});
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx
deleted file mode 100644
index 2ceaa2d1b68ef..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { EuiLoadingSpinner } from '@elastic/eui';
-import React, { useEffect, FC } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
-import { Ping } from '../../../../common/runtime_types';
-import { getJourneySteps } from '../../../state/actions/journey';
-import { JourneyState } from '../../../state/reducers/journey';
-import { journeySelector } from '../../../state/selectors';
-import { EmptyJourney } from './empty_journey';
-import { ExecutedJourney } from './executed_journey';
-import { ConsoleOutputEventList } from './console_output_event_list';
-
-interface BrowserExpandedRowProps {
- checkGroup?: string;
-}
-
-export const BrowserExpandedRow: React.FC = ({ checkGroup }) => {
- const dispatch = useDispatch();
- useEffect(() => {
- if (checkGroup) {
- dispatch(getJourneySteps({ checkGroup }));
- }
- }, [dispatch, checkGroup]);
-
- const journeys = useSelector(journeySelector);
- const journey = journeys[checkGroup ?? ''];
-
- return ;
-};
-
-type ComponentProps = BrowserExpandedRowProps & {
- journey?: JourneyState;
-};
-
-const stepEnd = (step: Ping) => step.synthetics?.type === 'step/end';
-const stepConsole = (step: Ping) =>
- ['stderr', 'cmd/status'].indexOf(step.synthetics?.type ?? '') !== -1;
-
-export const BrowserExpandedRowComponent: FC = ({ checkGroup, journey }) => {
- if (!!journey && journey.loading) {
- return (
-
-
-
- );
- }
-
- if (!journey || journey.steps.length === 0) {
- return ;
- }
-
- if (journey.steps.some(stepEnd)) return ;
-
- if (journey.steps.some(stepConsole)) return ;
-
- // TODO: should not happen, this means that the journey has no step/end and no console logs, but some other steps; filmstrip, screenshot, etc.
- // we should probably create an error prompt letting the user know this step is not supported yet
- return null;
-};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx
deleted file mode 100644
index 2fbc19d245826..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx
+++ /dev/null
@@ -1,265 +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 { shallowWithIntl } from '@kbn/test/jest';
-import React from 'react';
-import { ExecutedJourney } from './executed_journey';
-import { Ping } from '../../../../common/runtime_types';
-
-const MONITOR_BOILERPLATE = {
- id: 'MON_ID',
- duration: {
- us: 10,
- },
- status: 'down',
- type: 'browser',
-};
-
-describe('ExecutedJourney component', () => {
- let steps: Ping[];
-
- beforeEach(() => {
- steps = [
- {
- docId: '1',
- timestamp: '123',
- monitor: MONITOR_BOILERPLATE,
- synthetics: {
- payload: {
- status: 'failed',
- },
- type: 'step/end',
- },
- },
- {
- docId: '2',
- timestamp: '124',
- monitor: MONITOR_BOILERPLATE,
- synthetics: {
- payload: {
- status: 'failed',
- },
- type: 'step/end',
- },
- },
- ];
- });
-
- it('creates expected message for all failed', () => {
- const wrapper = shallowWithIntl(
-
- );
- expect(wrapper.find('EuiText')).toMatchInlineSnapshot(`
-
-
-
-
-
- 2 Steps - all failed or skipped
-
-
- `);
- });
-
- it('creates expected message for all succeeded', () => {
- steps[0].synthetics!.payload!.status = 'succeeded';
- steps[1].synthetics!.payload!.status = 'succeeded';
- const wrapper = shallowWithIntl(
-
- );
- expect(wrapper.find('EuiText')).toMatchInlineSnapshot(`
-
-
-
-
-
- 2 Steps - all succeeded
-
-
- `);
- });
-
- it('creates appropriate message for mixed results', () => {
- steps[0].synthetics!.payload!.status = 'succeeded';
- const wrapper = shallowWithIntl(
-
- );
- expect(wrapper.find('EuiText')).toMatchInlineSnapshot(`
-
-
-
-
-
- 2 Steps - 1 succeeded
-
-
- `);
- });
-
- it('tallies skipped steps', () => {
- steps[0].synthetics!.payload!.status = 'succeeded';
- steps[1].synthetics!.payload!.status = 'skipped';
- const wrapper = shallowWithIntl(
-
- );
- expect(wrapper.find('EuiText')).toMatchInlineSnapshot(`
-
-
-
-
-
- 2 Steps - 1 succeeded
-
-
- `);
- });
-
- it('uses appropriate count when non-step/end steps are included', () => {
- steps[0].synthetics!.payload!.status = 'succeeded';
- steps.push({
- docId: '3',
- timestamp: '125',
- monitor: MONITOR_BOILERPLATE,
- synthetics: {
- type: 'stderr',
- error: {
- message: `there was an error, that's all we know`,
- stack: 'your.error.happened.here',
- },
- },
- });
- const wrapper = shallowWithIntl(
-
- );
- expect(wrapper.find('EuiText')).toMatchInlineSnapshot(`
-
-
-
-
-
- 2 Steps - 1 succeeded
-
-
- `);
- });
-
- it('renders a component per step', () => {
- expect(
- shallowWithIntl(
-
- ).find('EuiFlexGroup')
- ).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
- });
-});
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx
deleted file mode 100644
index 1ded7f065d8ab..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx
+++ /dev/null
@@ -1,88 +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, EuiSpacer, EuiText } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import React, { FC } from 'react';
-import { Ping } from '../../../../common/runtime_types';
-import { JourneyState } from '../../../state/reducers/journey';
-import { ExecutedStep } from './executed_step';
-
-interface StepStatusCount {
- failed: number;
- skipped: number;
- succeeded: number;
-}
-
-function statusMessage(count: StepStatusCount) {
- const total = count.succeeded + count.failed + count.skipped;
- if (count.failed + count.skipped === total) {
- return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', {
- defaultMessage: '{total} Steps - all failed or skipped',
- values: { total },
- });
- } else if (count.succeeded === total) {
- return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', {
- defaultMessage: '{total} Steps - all succeeded',
- values: { total },
- });
- }
- return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', {
- defaultMessage: '{total} Steps - {succeeded} succeeded',
- values: { succeeded: count.succeeded, total },
- });
-}
-
-function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount {
- if (cur.synthetics?.payload?.status === 'succeeded') {
- prev.succeeded += 1;
- return prev;
- } else if (cur.synthetics?.payload?.status === 'skipped') {
- prev.skipped += 1;
- return prev;
- }
- prev.failed += 1;
- return prev;
-}
-
-function isStepEnd(step: Ping) {
- return step.synthetics?.type === 'step/end';
-}
-
-interface ExecutedJourneyProps {
- journey: JourneyState;
-}
-
-export const ExecutedJourney: FC = ({ journey }) => {
- return (
-
-
-
-
-
-
- {statusMessage(
- journey.steps
- .filter(isStepEnd)
- .reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 })
- )}
-
-
-
-
- {journey.steps.filter(isStepEnd).map((step, index) => (
-
- ))}
-
-
-
- );
-};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
deleted file mode 100644
index 991aa8fefba0a..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx
+++ /dev/null
@@ -1,110 +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 { EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
-import React, { FC } from 'react';
-import { i18n } from '@kbn/i18n';
-import { CodeBlockAccordion } from './code_block_accordion';
-import { StepScreenshotDisplay } from './step_screenshot_display';
-import { StatusBadge } from './status_badge';
-import { Ping } from '../../../../common/runtime_types';
-import { StepDetailLink } from '../../common/step_detail_link';
-import { VIEW_PERFORMANCE } from './translations';
-
-const CODE_BLOCK_OVERFLOW_HEIGHT = 360;
-
-interface ExecutedStepProps {
- step: Ping;
- index: number;
- checkGroup: string;
-}
-
-export const ExecutedStep: FC = ({ step, index, checkGroup }) => {
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {step.synthetics?.step?.index && (
-
-
- {VIEW_PERFORMANCE}
-
-
-
- )}
-
- {step.synthetics?.payload?.source}
-
-
- {step.synthetics?.error?.message}
-
-
- {step.synthetics?.error?.stack}
-
-
-
-
-
- >
- );
-};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx
deleted file mode 100644
index 304787e96818f..0000000000000
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { shallowWithIntl } from '@kbn/test/jest';
-import React from 'react';
-import { StatusBadge } from './status_badge';
-
-describe('StatusBadge', () => {
- it('displays success message', () => {
- expect(shallowWithIntl( )).toMatchInlineSnapshot(`
-
- Succeeded
-
- `);
- });
-
- it('displays failed message', () => {
- expect(shallowWithIntl( )).toMatchInlineSnapshot(`
-
- Failed
-
- `);
- });
-
- it('displays skipped message', () => {
- expect(shallowWithIntl( )).toMatchInlineSnapshot(`
-
- Skipped
-
- `);
- });
-});
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
index 346af9d31a28b..ef0d001ac905e 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
@@ -48,7 +48,7 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex })
};
}, [stepIndex, journey]);
- useMonitorBreadcrumb({ journey, activeStep });
+ useMonitorBreadcrumb({ details: journey?.details, activeStep, performanceBreakDownView: true });
const handleNextStep = useCallback(() => {
history.push(`/journey/${checkGroup}/step/${stepIndex + 1}`);
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx
index c51b85f76d605..8b85f05130d0b 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx
@@ -6,20 +6,25 @@
*/
import moment from 'moment';
+import { i18n } from '@kbn/i18n';
import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs';
-import { useKibana, useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import { JourneyState } from '../../../../state/reducers/journey';
import { Ping } from '../../../../../common/runtime_types/ping';
import { PLUGIN } from '../../../../../common/constants/plugin';
+import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column';
interface Props {
- journey: JourneyState;
+ details: JourneyState['details'];
activeStep?: Ping;
+ performanceBreakDownView?: boolean;
}
-export const useMonitorBreadcrumb = ({ journey, activeStep }: Props) => {
- const [dateFormat] = useUiSetting$('dateFormat');
-
+export const useMonitorBreadcrumb = ({
+ details,
+ activeStep,
+ performanceBreakDownView = false,
+}: Props) => {
const kibana = useKibana();
const appPath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? '';
@@ -32,8 +37,22 @@ export const useMonitorBreadcrumb = ({ journey, activeStep }: Props) => {
},
]
: []),
- ...(journey?.details?.timestamp
- ? [{ text: moment(journey?.details?.timestamp).format(dateFormat) }]
+ ...(details?.journey?.monitor?.check_group
+ ? [
+ {
+ text: getShortTimeStamp(moment(details?.timestamp)),
+ href: `${appPath}/journey/${details.journey.monitor.check_group}/steps`,
+ },
+ ]
+ : []),
+ ...(performanceBreakDownView
+ ? [
+ {
+ text: i18n.translate('xpack.uptime.synthetics.performanceBreakDown.label', {
+ defaultMessage: 'Performance breakdown',
+ }),
+ },
+ ]
: []),
]);
};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx
index ac79d7f4c2a8a..4aed073424788 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx
@@ -17,7 +17,7 @@ import { JourneyState } from '../../../../state/reducers/journey';
import { chromeServiceMock, uiSettingsServiceMock } from 'src/core/public/mocks';
describe('useMonitorBreadcrumbs', () => {
- it('sets the given breadcrumbs', () => {
+ it('sets the given breadcrumbs for steps list view', () => {
let breadcrumbObj: ChromeBreadcrumb[] = [];
const getBreadcrumbs = () => {
return breadcrumbObj;
@@ -43,8 +43,13 @@ describe('useMonitorBreadcrumbs', () => {
const Component = () => {
useMonitorBreadcrumb({
- activeStep: { monitor: { id: 'test-monitor' } } as Ping,
- journey: { details: { timestamp: '2021-01-04T11:25:19.104Z' } } as JourneyState,
+ activeStep: { monitor: { id: 'test-monitor', check_group: 'fake-test-group' } } as Ping,
+ details: {
+ timestamp: '2021-01-04T11:25:19.104Z',
+ journey: {
+ monitor: { id: 'test-monitor', check_group: 'fake-test-group' },
+ },
+ } as JourneyState['details'],
});
return <>Step Water Fall>;
};
@@ -69,7 +74,78 @@ describe('useMonitorBreadcrumbs', () => {
"text": "test-monitor",
},
Object {
- "text": "Jan 4, 2021 @ 06:25:19.104",
+ "href": "/app/uptime/journey/fake-test-group/steps",
+ "onClick": [Function],
+ "text": "Jan 4, 2021 6:25:19 AM",
+ },
+ ]
+ `);
+ });
+
+ it('sets the given breadcrumbs for performance breakdown page', () => {
+ let breadcrumbObj: ChromeBreadcrumb[] = [];
+ const getBreadcrumbs = () => {
+ return breadcrumbObj;
+ };
+
+ const core = {
+ chrome: {
+ ...chromeServiceMock.createStartContract(),
+ setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => {
+ breadcrumbObj = newBreadcrumbs;
+ },
+ },
+ uiSettings: {
+ ...uiSettingsServiceMock.createSetupContract(),
+ get(key: string, defaultOverride?: any): any {
+ return `MMM D, YYYY @ HH:mm:ss.SSS` || defaultOverride;
+ },
+ get$(key: string, defaultOverride?: any): any {
+ return of(`MMM D, YYYY @ HH:mm:ss.SSS`) || of(defaultOverride);
+ },
+ },
+ };
+
+ const Component = () => {
+ useMonitorBreadcrumb({
+ activeStep: { monitor: { id: 'test-monitor', check_group: 'fake-test-group' } } as Ping,
+ details: {
+ timestamp: '2021-01-04T11:25:19.104Z',
+ journey: {
+ monitor: { id: 'test-monitor', check_group: 'fake-test-group' },
+ },
+ } as JourneyState['details'],
+ performanceBreakDownView: true,
+ });
+ return <>Step Water Fall>;
+ };
+
+ render(
+
+
+ ,
+ { core }
+ );
+
+ expect(getBreadcrumbs()).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "href": "/app/uptime",
+ "onClick": [Function],
+ "text": "Uptime",
+ },
+ Object {
+ "href": "/app/uptime/monitor/dGVzdC1tb25pdG9y",
+ "onClick": [Function],
+ "text": "test-monitor",
+ },
+ Object {
+ "href": "/app/uptime/journey/fake-test-group/steps",
+ "onClick": [Function],
+ "text": "Jan 4, 2021 6:25:19 AM",
+ },
+ Object {
+ "text": "Performance breakdown",
},
]
`);
diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
index c6476a5bf2e53..f5581f75b3759 100644
--- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx
@@ -67,7 +67,7 @@ export const getShortTimeStamp = (timeStamp: moment.Moment, relative = false) =>
moment.locale(prevLocale);
return shortTimestamp;
} else {
- if (moment().diff(timeStamp, 'd') > 1) {
+ if (moment().diff(timeStamp, 'd') >= 1) {
return timeStamp.format('ll LTS');
}
return timeStamp.format('LTS');
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx
new file mode 100644
index 0000000000000..16068e0d72b46
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { ReactRouterEuiLink } from '../../../common/react_router_helpers';
+import { Ping } from '../../../../../common/runtime_types/ping';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
+
+const LabelLink = euiStyled.div`
+ margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs};
+ font-size: ${({ theme }) => theme.eui.euiFontSizeS};
+`;
+
+interface Props {
+ lastSuccessfulStep: Ping;
+}
+
+export const ScreenshotLink = ({ lastSuccessfulStep }: Props) => {
+ return (
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx
new file mode 100644
index 0000000000000..eb7bc95751557
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx
@@ -0,0 +1,86 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import moment from 'moment';
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { StepScreenshotDisplay } from '../../step_screenshot_display';
+import { Ping } from '../../../../../common/runtime_types/ping';
+import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
+import { useFetcher } from '../../../../../../observability/public';
+import { fetchLastSuccessfulStep } from '../../../../state/api/journey';
+import { ScreenshotLink } from './screenshot_link';
+import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column';
+
+const Label = euiStyled.div`
+ margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs};
+ font-size: ${({ theme }) => theme.eui.euiFontSizeS};
+ color: ${({ theme }) => theme.eui.euiColorDarkShade};
+`;
+
+interface Props {
+ step: Ping;
+}
+
+export const StepScreenshots = ({ step }: Props) => {
+ const isSucceeded = step.synthetics?.payload?.status === 'succeeded';
+
+ const { data: lastSuccessfulStep } = useFetcher(() => {
+ if (!isSucceeded) {
+ return fetchLastSuccessfulStep({
+ timestamp: step.timestamp,
+ monitorId: step.monitor.id,
+ stepIndex: step.synthetics?.step?.index!,
+ });
+ }
+ }, [step.docId, step.timestamp]);
+
+ return (
+
+
+
+ {step.synthetics?.payload?.status !== 'succeeded' ? (
+
+ ) : (
+
+ )}
+
+
+
+ {getShortTimeStamp(moment(step.timestamp))}
+
+ {!isSucceeded && lastSuccessfulStep?.monitor && (
+
+
+
+
+ {getShortTimeStamp(moment(lastSuccessfulStep.timestamp))}
+
+ )}
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx
new file mode 100644
index 0000000000000..69a5ef91a5925
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.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 { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+import { Ping } from '../../../../common/runtime_types/ping';
+import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp';
+
+interface Props {
+ step: Ping;
+}
+
+export const StepImage = ({ step }: Props) => {
+ return (
+
+
+
+
+
+ {step.synthetics?.step?.name}
+
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx
new file mode 100644
index 0000000000000..959bf0f644580
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx
@@ -0,0 +1,144 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { Ping } from '../../../../common/runtime_types/ping';
+import { StepsList } from './steps_list';
+import { render } from '../../../lib/helper/rtl_helpers';
+
+describe('StepList component', () => {
+ let steps: Ping[];
+
+ beforeEach(() => {
+ steps = [
+ {
+ docId: '1',
+ timestamp: '123',
+ monitor: {
+ id: 'MON_ID',
+ duration: {
+ us: 10,
+ },
+ status: 'down',
+ type: 'browser',
+ check_group: 'fake-group',
+ },
+ synthetics: {
+ payload: {
+ status: 'failed',
+ },
+ type: 'step/end',
+ step: {
+ name: 'load page',
+ index: 1,
+ },
+ },
+ },
+ {
+ docId: '2',
+ timestamp: '124',
+ monitor: {
+ id: 'MON_ID',
+ duration: {
+ us: 10,
+ },
+ status: 'down',
+ type: 'browser',
+ check_group: 'fake-group-1',
+ },
+ synthetics: {
+ payload: {
+ status: 'failed',
+ },
+ type: 'step/end',
+ step: {
+ name: 'go to login',
+ index: 2,
+ },
+ },
+ },
+ ];
+ });
+
+ it('creates expected message for all failed', () => {
+ const { getByText } = render( );
+ expect(getByText('2 Steps - all failed or skipped'));
+ });
+
+ it('renders a link to the step detail view', () => {
+ const { getByTitle, getByTestId } = render( );
+ expect(getByTestId('step-detail-link')).toHaveAttribute('href', '/journey/fake-group/step/1');
+ expect(getByTitle(`Failed`));
+ });
+
+ it.each([
+ ['succeeded', 'Succeeded'],
+ ['failed', 'Failed'],
+ ['skipped', 'Skipped'],
+ ])('supplies status badge correct status', (status, expectedStatus) => {
+ const step = steps[0];
+ step.synthetics!.payload!.status = status;
+ const { getByText } = render( );
+ expect(getByText(expectedStatus));
+ });
+
+ it('creates expected message for all succeeded', () => {
+ steps[0].synthetics!.payload!.status = 'succeeded';
+ steps[1].synthetics!.payload!.status = 'succeeded';
+
+ const { getByText } = render( );
+ expect(getByText('2 Steps - all succeeded'));
+ });
+
+ it('creates appropriate message for mixed results', () => {
+ steps[0].synthetics!.payload!.status = 'succeeded';
+
+ const { getByText } = render( );
+ expect(getByText('2 Steps - 1 succeeded'));
+ });
+
+ it('tallies skipped steps', () => {
+ steps[0].synthetics!.payload!.status = 'succeeded';
+ steps[1].synthetics!.payload!.status = 'skipped';
+
+ const { getByText } = render( );
+ expect(getByText('2 Steps - 1 succeeded'));
+ });
+
+ it('uses appropriate count when non-step/end steps are included', () => {
+ steps[0].synthetics!.payload!.status = 'succeeded';
+ steps.push({
+ docId: '3',
+ timestamp: '125',
+ monitor: {
+ id: 'MON_ID',
+ duration: {
+ us: 10,
+ },
+ status: 'down',
+ type: 'browser',
+ check_group: 'fake-group-2',
+ },
+ synthetics: {
+ type: 'stderr',
+ error: {
+ message: `there was an error, that's all we know`,
+ stack: 'your.error.happened.here',
+ },
+ },
+ });
+
+ const { getByText } = render( );
+ expect(getByText('2 Steps - 1 succeeded'));
+ });
+
+ it('renders a row per step', () => {
+ const { getByTestId } = render( );
+ expect(getByTestId('row-fake-group'));
+ expect(getByTestId('row-fake-group-1'));
+ });
+});
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx
new file mode 100644
index 0000000000000..47bf3ae0a1784
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx
@@ -0,0 +1,175 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiBasicTable, EuiButtonIcon, EuiPanel, EuiTitle } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React, { MouseEvent } from 'react';
+import styled from 'styled-components';
+import { Ping } from '../../../../common/runtime_types';
+import { STATUS_LABEL } from '../../monitor/ping_list/translations';
+import { COLLAPSE_LABEL, EXPAND_LABEL, STEP_NAME_LABEL } from '../translations';
+import { StatusBadge } from '../status_badge';
+import { StepDetailLink } from '../../common/step_detail_link';
+import { VIEW_PERFORMANCE } from '../../monitor/synthetics/translations';
+import { StepImage } from './step_image';
+import { useExpandedRow } from './use_expanded_row';
+
+export const SpanWithMargin = styled.span`
+ margin-right: 16px;
+`;
+
+interface Props {
+ data: Ping[];
+ error?: Error;
+ loading: boolean;
+}
+
+interface StepStatusCount {
+ failed: number;
+ skipped: number;
+ succeeded: number;
+}
+
+function isStepEnd(step: Ping) {
+ return step.synthetics?.type === 'step/end';
+}
+
+function statusMessage(count: StepStatusCount, loading?: boolean) {
+ if (loading) {
+ return i18n.translate('xpack.uptime.synthetics.journey.loadingSteps', {
+ defaultMessage: 'Loading steps ...',
+ });
+ }
+ const total = count.succeeded + count.failed + count.skipped;
+ if (count.failed + count.skipped === total) {
+ return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', {
+ defaultMessage: '{total} Steps - all failed or skipped',
+ values: { total },
+ });
+ } else if (count.succeeded === total) {
+ return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', {
+ defaultMessage: '{total} Steps - all succeeded',
+ values: { total },
+ });
+ }
+ return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', {
+ defaultMessage: '{total} Steps - {succeeded} succeeded',
+ values: { succeeded: count.succeeded, total },
+ });
+}
+
+function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount {
+ if (cur.synthetics?.payload?.status === 'succeeded') {
+ prev.succeeded += 1;
+ return prev;
+ } else if (cur.synthetics?.payload?.status === 'skipped') {
+ prev.skipped += 1;
+ return prev;
+ }
+ prev.failed += 1;
+ return prev;
+}
+
+export const StepsList = ({ data, error, loading }: Props) => {
+ const steps = data.filter(isStepEnd);
+
+ const { expandedRows, toggleExpand } = useExpandedRow({ steps, allPings: data, loading });
+
+ const columns: any[] = [
+ {
+ field: 'synthetics.payload.status',
+ name: STATUS_LABEL,
+ render: (pingStatus: string, item: Ping) => (
+
+ ),
+ },
+ {
+ align: 'left',
+ field: 'timestamp',
+ name: STEP_NAME_LABEL,
+ render: (timestamp: string, item: Ping) => ,
+ },
+ {
+ align: 'left',
+ field: 'timestamp',
+ name: '',
+ render: (val: string, item: Ping) => (
+
+ {VIEW_PERFORMANCE}
+
+ ),
+ },
+ {
+ align: 'right',
+ width: '24px',
+ isExpander: true,
+ render: (ping: Ping) => {
+ return (
+ toggleExpand({ ping })}
+ aria-label={expandedRows[ping.docId] ? COLLAPSE_LABEL : EXPAND_LABEL}
+ iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'}
+ />
+ );
+ },
+ },
+ ];
+
+ const getRowProps = (item: Ping) => {
+ const { monitor } = item;
+
+ return {
+ height: '85px',
+ 'data-test-subj': `row-${monitor.check_group}`,
+ onClick: (evt: MouseEvent) => {
+ const targetElem = evt.target as HTMLElement;
+
+ // we dont want to capture image click event
+ if (targetElem.tagName !== 'IMG' && targetElem.tagName !== 'BUTTON') {
+ toggleExpand({ ping: item });
+ }
+ },
+ };
+ };
+
+ return (
+
+
+
+ {statusMessage(
+ steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }),
+ loading
+ )}
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts
new file mode 100644
index 0000000000000..da40b900fdcc2
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useParams } from 'react-router-dom';
+import { FETCH_STATUS, useFetcher } from '../../../../../observability/public';
+import { fetchJourneySteps } from '../../../state/api/journey';
+import { JourneyState } from '../../../state/reducers/journey';
+
+export const useCheckSteps = (): JourneyState => {
+ const { checkGroupId } = useParams<{ checkGroupId: string }>();
+
+ const { data, status, error } = useFetcher(() => {
+ return fetchJourneySteps({
+ checkGroup: checkGroupId,
+ });
+ }, [checkGroupId]);
+
+ return {
+ error,
+ checkGroup: checkGroupId,
+ steps: data?.steps ?? [],
+ details: data?.details,
+ loading: status === FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING,
+ };
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx
new file mode 100644
index 0000000000000..d94122a7311ca
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx
@@ -0,0 +1,152 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { Route } from 'react-router-dom';
+import { fireEvent, screen } from '@testing-library/dom';
+import { EuiButtonIcon } from '@elastic/eui';
+import { createMemoryHistory } from 'history';
+
+import { useExpandedRow } from './use_expanded_row';
+import { render } from '../../../lib/helper/rtl_helpers';
+import { Ping } from '../../../../common/runtime_types/ping';
+import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../../common/constants';
+import { COLLAPSE_LABEL, EXPAND_LABEL } from '../translations';
+import { act } from 'react-dom/test-utils';
+
+describe('useExpandedROw', () => {
+ let expandedRowsObj = {};
+ const TEST_ID = 'uptimeStepListExpandBtn';
+
+ const history = createMemoryHistory({
+ initialEntries: ['/journey/fake-group/steps'],
+ });
+ const steps: Ping[] = [
+ {
+ docId: '1',
+ timestamp: '123',
+ monitor: {
+ id: 'MON_ID',
+ duration: {
+ us: 10,
+ },
+ status: 'down',
+ type: 'browser',
+ check_group: 'fake-group',
+ },
+ synthetics: {
+ payload: {
+ status: 'failed',
+ },
+ type: 'step/end',
+ step: {
+ name: 'load page',
+ index: 1,
+ },
+ },
+ },
+ {
+ docId: '2',
+ timestamp: '124',
+ monitor: {
+ id: 'MON_ID',
+ duration: {
+ us: 10,
+ },
+ status: 'down',
+ type: 'browser',
+ check_group: 'fake-group',
+ },
+ synthetics: {
+ payload: {
+ status: 'failed',
+ },
+ type: 'step/end',
+ step: {
+ name: 'go to login',
+ index: 2,
+ },
+ },
+ },
+ ];
+
+ const Component = () => {
+ const { expandedRows, toggleExpand } = useExpandedRow({
+ steps,
+ allPings: steps,
+ loading: false,
+ });
+
+ expandedRowsObj = expandedRows;
+
+ return (
+
+ Step list
+ {steps.map((ping, index) => (
+ toggleExpand({ ping })}
+ aria-label={expandedRows[ping.docId] ? COLLAPSE_LABEL : EXPAND_LABEL}
+ iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'}
+ />
+ ))}
+
+ );
+ };
+
+ it('it toggles rows on expand click', async () => {
+ render( , {
+ history,
+ });
+
+ fireEvent.click(await screen.findByTestId(TEST_ID + '1'));
+
+ expect(Object.keys(expandedRowsObj)).toStrictEqual(['1']);
+
+ expect(JSON.stringify(expandedRowsObj)).toContain('fake-group');
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId(TEST_ID + '1'));
+ });
+
+ expect(Object.keys(expandedRowsObj)).toStrictEqual([]);
+ });
+
+ it('it can expand both rows at same time', async () => {
+ render( , {
+ history,
+ });
+
+ // let's expand both rows
+ fireEvent.click(await screen.findByTestId(TEST_ID + '1'));
+ fireEvent.click(await screen.findByTestId(TEST_ID + '0'));
+
+ expect(Object.keys(expandedRowsObj)).toStrictEqual(['0', '1']);
+ });
+
+ it('it updates already expanded rows on new check group monitor', async () => {
+ render( , {
+ history,
+ });
+
+ // let's expand both rows
+ fireEvent.click(await screen.findByTestId(TEST_ID + '1'));
+ fireEvent.click(await screen.findByTestId(TEST_ID + '0'));
+
+ const newFakeGroup = 'new-fake-group-1';
+
+ steps[0].monitor.check_group = newFakeGroup;
+ steps[1].monitor.check_group = newFakeGroup;
+
+ act(() => {
+ history.push(`/journey/${newFakeGroup}/steps`);
+ });
+
+ expect(JSON.stringify(expandedRowsObj)).toContain(newFakeGroup);
+ });
+});
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx
new file mode 100644
index 0000000000000..bb56b237dfbd2
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx
@@ -0,0 +1,88 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useEffect, useState, useCallback } from 'react';
+import { useParams } from 'react-router-dom';
+import { ExecutedStep } from '../executed_step';
+import { Ping } from '../../../../common/runtime_types/ping';
+
+interface HookProps {
+ loading: boolean;
+ allPings: Ping[];
+ steps: Ping[];
+}
+
+type ExpandRowType = Record;
+
+export const useExpandedRow = ({ loading, steps, allPings }: HookProps) => {
+ const [expandedRows, setExpandedRows] = useState({});
+ // eui table uses index from 0, synthetics uses 1
+
+ const { checkGroupId } = useParams<{ checkGroupId: string }>();
+
+ const getBrowserConsole = useCallback(
+ (index: number) => {
+ return allPings.find(
+ (stepF) =>
+ stepF.synthetics?.type === 'journey/browserconsole' &&
+ stepF.synthetics?.step?.index! === index
+ )?.synthetics?.payload?.text;
+ },
+ [allPings]
+ );
+
+ useEffect(() => {
+ const expandedRowsN: ExpandRowType = {};
+ for (const expandedRowKeyStr in expandedRows) {
+ if (expandedRows.hasOwnProperty(expandedRowKeyStr)) {
+ const expandedRowKey = Number(expandedRowKeyStr);
+
+ const step = steps.find((stepF) => stepF.synthetics?.step?.index !== expandedRowKey)!;
+
+ expandedRowsN[expandedRowKey] = (
+
+ );
+ }
+ }
+
+ setExpandedRows(expandedRowsN);
+
+ // we only want to update when checkGroupId changes
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [checkGroupId, loading]);
+
+ const toggleExpand = ({ ping }: { ping: Ping }) => {
+ // eui table uses index from 0, synthetics uses 1
+ const stepIndex = ping.synthetics?.step?.index! - 1;
+
+ // If already expanded, collapse
+ if (expandedRows[stepIndex]) {
+ delete expandedRows[stepIndex];
+ setExpandedRows({ ...expandedRows });
+ } else {
+ // Otherwise expand this row
+ setExpandedRows({
+ ...expandedRows,
+ [stepIndex]: (
+
+ ),
+ });
+ }
+ };
+
+ return { expandedRows, toggleExpand };
+};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx b/x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx
similarity index 87%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx
index 18aeb7a236ca8..225ba1041c263 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx
@@ -13,6 +13,7 @@ interface Props {
id?: string;
language: 'html' | 'javascript';
overflowHeight: number;
+ initialIsOpen?: boolean;
}
/**
@@ -25,9 +26,10 @@ export const CodeBlockAccordion: FC = ({
id,
language,
overflowHeight,
+ initialIsOpen = false,
}) => {
return children && id ? (
-
+
{children}
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_event.test.tsx
similarity index 100%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.test.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/console_event.test.tsx
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_event.tsx
similarity index 89%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/console_event.tsx
index dc7b6ce9ea123..19672f953607b 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/console_event.tsx
@@ -7,8 +7,8 @@
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import React, { useContext, FC } from 'react';
-import { Ping } from '../../../../common/runtime_types';
-import { UptimeThemeContext } from '../../../contexts';
+import { UptimeThemeContext } from '../../contexts';
+import { Ping } from '../../../common/runtime_types/ping';
interface Props {
event: Ping;
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.test.tsx
similarity index 100%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.test.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.test.tsx
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx
similarity index 92%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx
index df1f6aeb3623b..df4314e5ccf1c 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx
@@ -8,9 +8,9 @@
import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { FC } from 'react';
-import { Ping } from '../../../../common/runtime_types';
-import { JourneyState } from '../../../state/reducers/journey';
import { ConsoleEvent } from './console_event';
+import { Ping } from '../../../common/runtime_types/ping';
+import { JourneyState } from '../../state/reducers/journey';
interface Props {
journey: JourneyState;
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/empty_journey.test.tsx
similarity index 100%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.test.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/empty_journey.test.tsx
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx b/x-pack/plugins/uptime/public/components/synthetics/empty_journey.tsx
similarity index 100%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/empty_journey.tsx
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx
similarity index 54%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx
index 225ccb884ad00..24b52e09adbf9 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx
@@ -7,8 +7,8 @@
import React from 'react';
import { ExecutedStep } from './executed_step';
-import { Ping } from '../../../../common/runtime_types';
-import { render } from '../../../lib/helper/rtl_helpers';
+import { render } from '../../lib/helper/rtl_helpers';
+import { Ping } from '../../../common/runtime_types/ping';
describe('ExecutedStep', () => {
let step: Ping;
@@ -34,33 +34,6 @@ describe('ExecutedStep', () => {
};
});
- it('renders correct step heading', () => {
- const { getByText } = render( );
-
- expect(getByText(`${step?.synthetics?.step?.index}. ${step?.synthetics?.step?.name}`));
- });
-
- it('renders a link to the step detail view', () => {
- const { getByRole, getByText } = render(
-
- );
- expect(getByRole('link')).toHaveAttribute('href', '/journey/fake-group/step/4');
- expect(getByText('4. STEP_NAME'));
- });
-
- it.each([
- ['succeeded', 'Succeeded'],
- ['failed', 'Failed'],
- ['skipped', 'Skipped'],
- ['somegarbage', '4.'],
- ])('supplies status badge correct status', (status, expectedStatus) => {
- step.synthetics = {
- payload: { status },
- };
- const { getByText } = render( );
- expect(getByText(expectedStatus));
- });
-
it('renders accordion for step', () => {
step.synthetics = {
payload: {
@@ -72,10 +45,9 @@ describe('ExecutedStep', () => {
},
};
- const { getByText } = render( );
+ const { getByText } = render( );
- expect(getByText('4. STEP_NAME'));
- expect(getByText('Step script'));
+ expect(getByText('Script executed at this step'));
expect(getByText(`const someVar = "the var"`));
});
@@ -87,11 +59,22 @@ describe('ExecutedStep', () => {
},
};
- const { getByText } = render( );
+ const { getByText } = render( );
- expect(getByText('4.'));
- expect(getByText('Error'));
+ expect(getByText('Error message'));
expect(getByText('There was an error executing the step.'));
expect(getByText('some.stack.trace.string'));
});
+
+ it('renders accordions for console output', () => {
+ const browserConsole =
+ "Refused to execute script from because its MIME type ('image/gif') is not executable";
+
+ const { getByText } = render(
+
+ );
+
+ expect(getByText('Console output'));
+ expect(getByText(browserConsole));
+ });
});
diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx
new file mode 100644
index 0000000000000..a77b3dfe3ba21
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx
@@ -0,0 +1,118 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui';
+import React, { FC } from 'react';
+import { i18n } from '@kbn/i18n';
+import { CodeBlockAccordion } from './code_block_accordion';
+import { Ping } from '../../../common/runtime_types/ping';
+import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
+import { StepScreenshots } from './check_steps/step_expanded_row/step_screenshots';
+
+const CODE_BLOCK_OVERFLOW_HEIGHT = 360;
+
+interface ExecutedStepProps {
+ step: Ping;
+ index: number;
+ loading: boolean;
+ browserConsole?: string;
+}
+
+const Label = euiStyled.div`
+ margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs};
+ font-size: ${({ theme }) => theme.eui.euiFontSizeS};
+ color: ${({ theme }) => theme.eui.euiColorDarkShade};
+`;
+
+const Message = euiStyled.div`
+ font-weight: bold;
+ font-size:${({ theme }) => theme.eui.euiFontSizeM};
+ margin-bottom: ${(props) => props.theme.eui.paddingSizes.m};
+`;
+
+const ExpandedRow = euiStyled.div`
+ padding: '8px';
+ max-width: 1000px;
+ width: 100%;
+`;
+
+export const ExecutedStep: FC = ({
+ loading,
+ step,
+ index,
+ browserConsole = '',
+}) => {
+ const isSucceeded = step.synthetics?.payload?.status === 'succeeded';
+
+ return (
+
+ {loading ? (
+
+ ) : (
+ <>
+
+ {step.synthetics?.error?.message && (
+
+
+ {i18n.translate('xpack.uptime.synthetics.executedStep.errorHeading', {
+ defaultMessage: 'Error message',
+ })}
+
+ {step.synthetics?.error?.message}
+
+ )}
+
+
+ {step.synthetics?.payload?.source}
+
+
+
+ <>
+ {browserConsole}
+ >
+
+
+
+
+
+ {step.synthetics?.error?.stack}
+
+ >
+ )}
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx
new file mode 100644
index 0000000000000..500c680b91bf6
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license 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 { StatusBadge } from './status_badge';
+import { render } from '../../lib/helper/rtl_helpers';
+
+describe('StatusBadge', () => {
+ it('displays success message', () => {
+ const { getByText } = render( );
+
+ expect(getByText('1.'));
+ expect(getByText('Succeeded'));
+ });
+
+ it('displays failed message', () => {
+ const { getByText } = render( );
+
+ expect(getByText('2.'));
+ expect(getByText('Failed'));
+ });
+
+ it('displays skipped message', () => {
+ const { getByText } = render( );
+
+ expect(getByText('3.'));
+ expect(getByText('Skipped'));
+ });
+});
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx b/x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx
similarity index 66%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx
index 0cf9e5477d0db..b4c4e310abe6b 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx
@@ -5,14 +5,15 @@
* 2.0.
*/
-import { EuiBadge } from '@elastic/eui';
+import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useContext, FC } from 'react';
-import { UptimeAppColors } from '../../../apps/uptime_app';
-import { UptimeThemeContext } from '../../../contexts';
+import { UptimeAppColors } from '../../apps/uptime_app';
+import { UptimeThemeContext } from '../../contexts';
interface StatusBadgeProps {
status?: string;
+ stepNo: number;
}
export function colorFromStatus(color: UptimeAppColors, status?: string) {
@@ -45,9 +46,18 @@ export function textFromStatus(status?: string) {
}
}
-export const StatusBadge: FC = ({ status }) => {
+export const StatusBadge: FC = ({ status, stepNo }) => {
const theme = useContext(UptimeThemeContext);
return (
- {textFromStatus(status)}
+
+
+
+ {stepNo}.
+
+
+
+ {textFromStatus(status)}
+
+
);
};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx
similarity index 96%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx
index 29dca39c34bf2..52d2eacaf0e52 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx
@@ -5,9 +5,9 @@
* 2.0.
*/
-import { render } from '../../../lib/helper/rtl_helpers';
import React from 'react';
import { StepScreenshotDisplay } from './step_screenshot_display';
+import { render } from '../../lib/helper/rtl_helpers';
jest.mock('react-use/lib/useIntersection', () => () => ({
isIntersecting: true,
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx
similarity index 52%
rename from x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx
rename to x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx
index 654193de72a9c..78c65b7d40803 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx
+++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx
@@ -5,33 +5,32 @@
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiImage, EuiPopover, EuiText } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiImage, EuiText } from '@elastic/eui';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useContext, useEffect, useRef, useState, FC } from 'react';
import useIntersection from 'react-use/lib/useIntersection';
-import { UptimeSettingsContext, UptimeThemeContext } from '../../../contexts';
+import { UptimeSettingsContext, UptimeThemeContext } from '../../contexts';
interface StepScreenshotDisplayProps {
screenshotExists?: boolean;
checkGroup?: string;
stepIndex?: number;
stepName?: string;
+ lazyLoad?: boolean;
}
-const THUMBNAIL_WIDTH = 320;
-const THUMBNAIL_HEIGHT = 180;
-const POPOVER_IMG_WIDTH = 640;
-const POPOVER_IMG_HEIGHT = 360;
+const IMAGE_WIDTH = 640;
+const IMAGE_HEIGHT = 360;
const StepImage = styled(EuiImage)`
&&& {
figcaption {
display: none;
}
- width: ${THUMBNAIL_WIDTH},
- height: ${THUMBNAIL_HEIGHT},
+ width: ${IMAGE_WIDTH},
+ height: ${IMAGE_HEIGHT},
objectFit: 'cover',
objectPosition: 'center top',
}
@@ -42,6 +41,7 @@ export const StepScreenshotDisplay: FC = ({
screenshotExists,
stepIndex,
stepName,
+ lazyLoad = true,
}) => {
const containerRef = useRef(null);
const {
@@ -50,8 +50,6 @@ export const StepScreenshotDisplay: FC = ({
const { basePath } = useContext(UptimeSettingsContext);
- const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false);
-
const intersection = useIntersection(containerRef, {
root: null,
rootMargin: '0px',
@@ -69,57 +67,26 @@ export const StepScreenshotDisplay: FC = ({
let content: JSX.Element | null = null;
const imgSrc = basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`;
- if (hasIntersected && screenshotExists) {
+ if ((hasIntersected || !lazyLoad) && screenshotExists) {
content = (
- <>
- setIsImagePopoverOpen(true)}
- onMouseLeave={() => setIsImagePopoverOpen(false)}
- />
- }
- closePopover={() => setIsImagePopoverOpen(false)}
- isOpen={isImagePopoverOpen}
- >
-
-
- >
+
);
} else if (screenshotExists === false) {
content = (
@@ -148,7 +115,7 @@ export const StepScreenshotDisplay: FC = ({
return (
{content}
diff --git a/x-pack/plugins/uptime/public/components/synthetics/translations.ts b/x-pack/plugins/uptime/public/components/synthetics/translations.ts
new file mode 100644
index 0000000000000..743118574b325
--- /dev/null
+++ b/x-pack/plugins/uptime/public/components/synthetics/translations.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 { i18n } from '@kbn/i18n';
+
+export const STEP_NAME_LABEL = i18n.translate('xpack.uptime.stepList.stepName', {
+ defaultMessage: 'Step name',
+});
+
+export const COLLAPSE_LABEL = i18n.translate('xpack.uptime.stepList.collapseRow', {
+ defaultMessage: 'Collapse',
+});
+
+export const EXPAND_LABEL = i18n.translate('xpack.uptime.stepList.expandRow', {
+ defaultMessage: 'Expand',
+});
diff --git a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts
index da0f109747758..b9ec9cc5e5516 100644
--- a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts
+++ b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts
@@ -16,6 +16,7 @@ export enum UptimePage {
Settings = 'Settings',
Certificates = 'Certificates',
StepDetail = 'StepDetail',
+ SyntheticCheckStepsPage = 'SyntheticCheckStepsPage',
NotFound = '__not-found__',
}
diff --git a/x-pack/plugins/uptime/public/pages/index.ts b/x-pack/plugins/uptime/public/pages/index.ts
index 828942bc1eb1e..5624f61c3abb5 100644
--- a/x-pack/plugins/uptime/public/pages/index.ts
+++ b/x-pack/plugins/uptime/public/pages/index.ts
@@ -6,6 +6,6 @@
*/
export { MonitorPage } from './monitor';
-export { StepDetailPage } from './step_detail_page';
+export { StepDetailPage } from './synthetics/step_detail_page';
export { SettingsPage } from './settings';
export { NotFoundPage } from './not_found';
diff --git a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx
new file mode 100644
index 0000000000000..291019d93c398
--- /dev/null
+++ b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useHistory } from 'react-router-dom';
+import moment from 'moment';
+import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types/ping';
+import { getShortTimeStamp } from '../../components/overview/monitor_list/columns/monitor_status_column';
+
+interface Props {
+ timestamp: string;
+ details: SyntheticsJourneyApiResponse['details'];
+}
+
+export const ChecksNavigation = ({ timestamp, details }: Props) => {
+ const history = useHistory();
+
+ return (
+
+
+ {
+ history.push(`/journey/${details?.previous?.checkGroup}/steps`);
+ }}
+ >
+
+
+
+
+ {getShortTimeStamp(moment(timestamp))}
+
+
+ {
+ history.push(`/journey/${details?.next?.checkGroup}/steps`);
+ }}
+ >
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/uptime/public/pages/step_detail_page.tsx b/x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx
similarity index 75%
rename from x-pack/plugins/uptime/public/pages/step_detail_page.tsx
rename to x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx
index aa81ddd0eae3d..de38d2d663523 100644
--- a/x-pack/plugins/uptime/public/pages/step_detail_page.tsx
+++ b/x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx
@@ -7,9 +7,9 @@
import React from 'react';
import { useParams } from 'react-router-dom';
-import { useTrackPageview } from '../../../observability/public';
-import { useInitApp } from '../hooks/use_init_app';
-import { StepDetailContainer } from '../components/monitor/synthetics/step_detail/step_detail_container';
+import { useTrackPageview } from '../../../../observability/public';
+import { useInitApp } from '../../hooks/use_init_app';
+import { StepDetailContainer } from '../../components/monitor/synthetics/step_detail/step_detail_container';
export const StepDetailPage: React.FC = () => {
useInitApp();
diff --git a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx
new file mode 100644
index 0000000000000..edfd7ae24f91b
--- /dev/null
+++ b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { useTrackPageview } from '../../../../observability/public';
+import { useInitApp } from '../../hooks/use_init_app';
+import { StepsList } from '../../components/synthetics/check_steps/steps_list';
+import { useCheckSteps } from '../../components/synthetics/check_steps/use_check_steps';
+import { ChecksNavigation } from './checks_navigation';
+import { useMonitorBreadcrumb } from '../../components/monitor/synthetics/step_detail/use_monitor_breadcrumb';
+import { EmptyJourney } from '../../components/synthetics/empty_journey';
+
+export const SyntheticsCheckSteps: React.FC = () => {
+ useInitApp();
+ useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps' });
+ useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps', delay: 15000 });
+
+ const { error, loading, steps, details, checkGroup } = useCheckSteps();
+
+ useMonitorBreadcrumb({ details, activeStep: details?.journey });
+
+ return (
+ <>
+
+
+
+ {details?.journey?.monitor.name || details?.journey?.monitor.id}
+
+
+
+ {details && }
+
+
+
+
+ {(!steps || steps.length === 0) && !loading && }
+ >
+ );
+};
diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx
index 82aa09c3293e6..dcfb21955f219 100644
--- a/x-pack/plugins/uptime/public/routes.tsx
+++ b/x-pack/plugins/uptime/public/routes.tsx
@@ -15,10 +15,12 @@ import {
OVERVIEW_ROUTE,
SETTINGS_ROUTE,
STEP_DETAIL_ROUTE,
+ SYNTHETIC_CHECK_STEPS_ROUTE,
} from '../common/constants';
import { MonitorPage, StepDetailPage, NotFoundPage, SettingsPage } from './pages';
import { CertificatesPage } from './pages/certificates';
import { UptimePage, useUptimeTelemetry } from './hooks';
+import { SyntheticsCheckSteps } from './pages/synthetics/synthetics_checks';
interface RouteProps {
path: string;
@@ -71,6 +73,13 @@ const Routes: RouteProps[] = [
dataTestSubj: 'uptimeStepDetailPage',
telemetryId: UptimePage.StepDetail,
},
+ {
+ title: baseTitle,
+ path: SYNTHETIC_CHECK_STEPS_ROUTE,
+ component: SyntheticsCheckSteps,
+ dataTestSubj: 'uptimeSyntheticCheckStepsPage',
+ telemetryId: UptimePage.SyntheticCheckStepsPage,
+ },
{
title: baseTitle,
path: OVERVIEW_ROUTE,
diff --git a/x-pack/plugins/uptime/public/state/api/journey.ts b/x-pack/plugins/uptime/public/state/api/journey.ts
index 5c4c7c7149792..63796a66d1c5c 100644
--- a/x-pack/plugins/uptime/public/state/api/journey.ts
+++ b/x-pack/plugins/uptime/public/state/api/journey.ts
@@ -8,6 +8,7 @@
import { apiService } from './utils';
import { FetchJourneyStepsParams } from '../actions/journey';
import {
+ Ping,
SyntheticsJourneyApiResponse,
SyntheticsJourneyApiResponseType,
} from '../../../common/runtime_types';
@@ -34,6 +35,22 @@ export async function fetchJourneysFailedSteps({
)) as SyntheticsJourneyApiResponse;
}
+export async function fetchLastSuccessfulStep({
+ monitorId,
+ timestamp,
+ stepIndex,
+}: {
+ monitorId: string;
+ timestamp: string;
+ stepIndex: number;
+}): Promise {
+ return (await apiService.get(`/api/uptime/synthetics/step/success/`, {
+ monitorId,
+ timestamp,
+ stepIndex,
+ })) as Ping;
+}
+
export async function getJourneyScreenshot(imgSrc: string) {
try {
const imgRequest = new Request(imgSrc);
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts
index e0edcc4576378..de37688b155f5 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts
@@ -27,13 +27,12 @@ export const getJourneyDetails: UMElasticsearchQueryFn<
},
{
term: {
- 'synthetics.type': 'journey/end',
+ 'synthetics.type': 'journey/start',
},
},
],
},
},
- _source: ['@timestamp', 'monitor.id'],
size: 1,
};
@@ -53,7 +52,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn<
},
{
term: {
- 'synthetics.type': 'journey/end',
+ 'synthetics.type': 'journey/start',
},
},
],
@@ -109,6 +108,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn<
nextJourneyResult?.hits?.hits.length > 0 ? nextJourneyResult?.hits?.hits[0] : null;
return {
timestamp: thisJourneySource['@timestamp'],
+ journey: thisJourneySource,
previous: previousJourney
? {
checkGroup: previousJourney._source.monitor.check_group,
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
index 9cb5e1eedb6b0..faa260eb9abd4 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
@@ -60,10 +60,10 @@ export const getJourneyScreenshot: UMElasticsearchQueryFn<
return null;
}
- const stepHit = result?.aggregations?.step.image.hits.hits[0]._source as Ping;
+ const stepHit = result?.aggregations?.step.image.hits.hits[0]?._source as Ping;
return {
- blob: stepHit.synthetics?.blob ?? null,
+ blob: stepHit?.synthetics?.blob ?? null,
stepName: stepHit?.synthetics?.step?.name ?? '',
totalSteps: result?.hits?.total.value,
};
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts
index 1034318257f66..af7752b05997e 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts
@@ -14,9 +14,9 @@ describe('getJourneySteps request module', () => {
expect(formatSyntheticEvents()).toMatchInlineSnapshot(`
Array [
"step/end",
- "stderr",
"cmd/status",
"step/screenshot",
+ "journey/browserconsole",
]
`);
});
@@ -121,9 +121,9 @@ describe('getJourneySteps request module', () => {
"terms": Object {
"synthetics.type": Array [
"step/end",
- "stderr",
"cmd/status",
"step/screenshot",
+ "journey/browserconsole",
],
},
}
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts
index 3055f169fc495..43d17cb938159 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts
@@ -13,7 +13,7 @@ export interface GetJourneyStepsParams {
syntheticEventTypes?: string | string[];
}
-const defaultEventTypes = ['step/end', 'stderr', 'cmd/status', 'step/screenshot'];
+const defaultEventTypes = ['step/end', 'cmd/status', 'step/screenshot', 'journey/browserconsole'];
export const formatSyntheticEvents = (eventTypes?: string | string[]) => {
if (!eventTypes) {
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts
new file mode 100644
index 0000000000000..82958167341c0
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.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 { UMElasticsearchQueryFn } from '../adapters/framework';
+import { Ping } from '../../../common/runtime_types/ping';
+
+export interface GetStepScreenshotParams {
+ monitorId: string;
+ timestamp: string;
+ stepIndex: number;
+}
+
+export const getStepLastSuccessfulStep: UMElasticsearchQueryFn<
+ GetStepScreenshotParams,
+ any
+> = async ({ uptimeEsClient, monitorId, stepIndex, timestamp }) => {
+ const lastSuccessCheckParams = {
+ size: 1,
+ sort: [
+ {
+ '@timestamp': {
+ order: 'desc',
+ },
+ },
+ ],
+ query: {
+ bool: {
+ filter: [
+ {
+ range: {
+ '@timestamp': {
+ lte: timestamp,
+ },
+ },
+ },
+ {
+ term: {
+ 'monitor.id': monitorId,
+ },
+ },
+ {
+ term: {
+ 'synthetics.type': 'step/end',
+ },
+ },
+ {
+ term: {
+ 'synthetics.step.status': 'succeeded',
+ },
+ },
+ {
+ term: {
+ 'synthetics.step.index': stepIndex,
+ },
+ },
+ ],
+ },
+ },
+ };
+
+ const { body: result } = await uptimeEsClient.search({ body: lastSuccessCheckParams });
+
+ if (result?.hits?.total.value < 1) {
+ return null;
+ }
+
+ const step = result?.hits.hits[0]._source as Ping & { '@timestamp': string };
+
+ return {
+ ...step,
+ timestamp: step['@timestamp'],
+ };
+};
diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts
index 9e665fb8bbdb0..24109245c2902 100644
--- a/x-pack/plugins/uptime/server/lib/requests/index.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/index.ts
@@ -24,6 +24,7 @@ import { getJourneyScreenshot } from './get_journey_screenshot';
import { getJourneyDetails } from './get_journey_details';
import { getNetworkEvents } from './get_network_events';
import { getJourneyFailedSteps } from './get_journey_failed_steps';
+import { getStepLastSuccessfulStep } from './get_last_successful_step';
export const requests = {
getCerts,
@@ -42,6 +43,7 @@ export const requests = {
getIndexStatus,
getJourneySteps,
getJourneyFailedSteps,
+ getStepLastSuccessfulStep,
getJourneyScreenshot,
getJourneyDetails,
getNetworkEvents,
diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts
index 41556d3c8d513..91b5597321ed0 100644
--- a/x-pack/plugins/uptime/server/rest_api/index.ts
+++ b/x-pack/plugins/uptime/server/rest_api/index.ts
@@ -27,6 +27,7 @@ import { createGetMonitorDurationRoute } from './monitors/monitors_durations';
import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state';
import { createNetworkEventsRoute } from './network_events';
import { createJourneyFailedStepsRoute } from './pings/journeys';
+import { createLastSuccessfulStepRoute } from './synthetics/last_successful_step';
export * from './types';
export { createRouteWithAuth } from './create_route_with_auth';
@@ -52,4 +53,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [
createJourneyScreenshotRoute,
createNetworkEventsRoute,
createJourneyFailedStepsRoute,
+ createLastSuccessfulStepRoute,
];
diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
index cda078da01539..2b056498d7f10 100644
--- a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
+++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
@@ -18,6 +18,9 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ
stepIndex: schema.number(),
_debug: schema.maybe(schema.boolean()),
}),
+ query: schema.object({
+ _debug: schema.maybe(schema.boolean()),
+ }),
},
handler: async ({ uptimeEsClient, request, response }) => {
const { checkGroup, stepIndex } = request.params;
@@ -28,7 +31,7 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ
stepIndex,
});
- if (result === null) {
+ if (result === null || !result.blob) {
return response.notFound();
}
return response.ok({
diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
index def373e88ae16..9b5bffc380c27 100644
--- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
+++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
@@ -15,7 +15,6 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =>
validate: {
params: schema.object({
checkGroup: schema.string(),
- _debug: schema.maybe(schema.boolean()),
}),
query: schema.object({
// provides a filter for the types of synthetic events to include
@@ -23,21 +22,24 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =>
syntheticEventTypes: schema.maybe(
schema.oneOf([schema.arrayOf(schema.string()), schema.string()])
),
+ _debug: schema.maybe(schema.boolean()),
}),
},
handler: async ({ uptimeEsClient, request }): Promise => {
const { checkGroup } = request.params;
const { syntheticEventTypes } = request.query;
- const result = await libs.requests.getJourneySteps({
- uptimeEsClient,
- checkGroup,
- syntheticEventTypes,
- });
- const details = await libs.requests.getJourneyDetails({
- uptimeEsClient,
- checkGroup,
- });
+ const [result, details] = await Promise.all([
+ await libs.requests.getJourneySteps({
+ uptimeEsClient,
+ checkGroup,
+ syntheticEventTypes,
+ }),
+ await libs.requests.getJourneyDetails({
+ uptimeEsClient,
+ checkGroup,
+ }),
+ ]);
return {
checkGroup,
@@ -53,6 +55,7 @@ export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMSer
validate: {
query: schema.object({
checkGroups: schema.arrayOf(schema.string()),
+ _debug: schema.maybe(schema.boolean()),
}),
},
handler: async ({ uptimeEsClient, request }): Promise => {
diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts b/x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts
new file mode 100644
index 0000000000000..a1523fae9d4a1
--- /dev/null
+++ b/x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { UMServerLibs } from '../../lib/lib';
+import { UMRestApiRouteFactory } from '../types';
+
+export const createLastSuccessfulStepRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
+ method: 'GET',
+ path: '/api/uptime/synthetics/step/success/',
+ validate: {
+ query: schema.object({
+ monitorId: schema.string(),
+ stepIndex: schema.number(),
+ timestamp: schema.string(),
+ _debug: schema.maybe(schema.boolean()),
+ }),
+ },
+ handler: async ({ uptimeEsClient, request, response }) => {
+ const { timestamp, monitorId, stepIndex } = request.query;
+
+ return await libs.requests.getStepLastSuccessfulStep({
+ uptimeEsClient,
+ monitorId,
+ stepIndex,
+ timestamp,
+ });
+ },
+});
From f90a4d95a52c7b337f0ebed319efe8a8c9de9014 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
<55978943+cauemarcondes@users.noreply.github.com>
Date: Tue, 16 Mar 2021 11:41:43 -0400
Subject: [PATCH 20/24] [APM] Telemetry: Add UI usage telemetry on the
Timeseries comparison feature (#91439)
* adding telemetry and removing time comparison
* adding telemetry and removing time comparison
* adding telemetry
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/app/transaction_overview/index.tsx | 1 -
.../components/shared/time_comparison/index.tsx | 15 ++++++++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
index 97be35ec6f5b9..0814c6d95b96a 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
@@ -83,7 +83,6 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) {
return (
<>
-
diff --git a/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx b/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx
index 1769119593c0e..84a2dad278a9b 100644
--- a/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx
@@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
import moment from 'moment';
import React from 'react';
import { useHistory } from 'react-router-dom';
+import { useUiTracker } from '../../../../../observability/public';
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
import { getDateDifference } from '../../../../common/utils/formatters';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
@@ -132,6 +133,7 @@ function getSelectOptions({
}
export function TimeComparison() {
+ const trackApmEvent = useUiTracker({ app: 'apm' });
const history = useHistory();
const { isMedium, isLarge } = useBreakPoints();
const {
@@ -181,9 +183,17 @@ export function TimeComparison() {
})}
checked={comparisonEnabled}
onChange={() => {
+ const nextComparisonEnabledValue = !comparisonEnabled;
+ if (nextComparisonEnabledValue === false) {
+ trackApmEvent({
+ metric: 'time_comparison_disabled',
+ });
+ }
urlHelpers.push(history, {
query: {
- comparisonEnabled: Boolean(!comparisonEnabled).toString(),
+ comparisonEnabled: Boolean(
+ nextComparisonEnabledValue
+ ).toString(),
},
});
}}
@@ -191,6 +201,9 @@ export function TimeComparison() {
}
onChange={(e) => {
+ trackApmEvent({
+ metric: `time_comparison_type_change_${e.target.value}`,
+ });
urlHelpers.push(history, {
query: {
comparisonType: e.target.value,
From bae6021d7f2b184229a2c2dfd628e3760ef3fb49 Mon Sep 17 00:00:00 2001
From: Devon Thomson
Date: Tue, 16 Mar 2021 11:55:11 -0400
Subject: [PATCH 21/24] [Save Modal] Quick Fix for Scrollbar (#94640)
* quick fixes for no scroll bar on save modals
---
.../panel_actions/customize_title/customize_panel_modal.tsx | 2 +-
.../__snapshots__/saved_object_save_modal.test.tsx.snap | 4 ++++
.../public/save_modal/saved_object_save_modal.tsx | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx
index 411da6c037900..6d5c2224c0635 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx
@@ -71,7 +71,7 @@ export class CustomizePanelModal extends Component {
return (
-
+
Customize panel
diff --git a/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap
index 1f05ed6b94405..91f99fd8e87dd 100644
--- a/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap
+++ b/src/plugins/saved_objects/public/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap
@@ -7,6 +7,7 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = `
onClose={[Function]}
>