From f3b74b457cb9b56dc922c29a5c3f496f485b40c3 Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger
Date: Tue, 16 Mar 2021 11:41:48 +0100
Subject: [PATCH 01/44] [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 02/44] [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 03/44] [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 04/44] [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 05/44] [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 06/44] 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 07/44] [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 08/44] 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 09/44] 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 10/44] [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 11/44] [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 12/44] [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 13/44] 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 14/44] [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 15/44] [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 (
+
+
+
+
+
+
+
+ {!isSucceeded && lastSuccessfulStep?.monitor && (
+
+
+
+
+
+
+ )}
+
+ );
+};
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 && (
+
+
+ {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 16/44] [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 17/44] [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]}
>