diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx index 78e46f6a0fea8..9442920eee1f6 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx @@ -75,6 +75,7 @@ export const Actions = ({ ', () => { const onUpdate = jest.fn(); - const useFetcher = spyOnUseFetcher({}); - it('calls delete monitor on monitor deletion', () => { - useFetcher.mockImplementation(originalUseFetcher); + it('calls delete monitor on monitor deletion', async () => { const deleteMonitor = jest.spyOn(fetchers, 'deleteMonitor'); const id = 'test-id'; - render(); + const store = createRealStore(); + render(, { + store, + }); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); expect(deleteMonitor).not.toBeCalled(); @@ -37,12 +39,24 @@ describe('', () => { fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); - expect(deleteMonitor).toBeCalledWith({ id }); + expect(dispatchSpy).toHaveBeenCalledWith({ + payload: { + id: 'test-id', + name: 'sample name', + }, + type: 'DELETE_MONITOR', + }); + + expect(store.getState().deleteMonitor.loading.includes(id)).toEqual(true); + + expect(await screen.findByLabelText('Loading')).toBeTruthy(); }); - it('calls set refresh when deletion is successful', () => { + it('calls set refresh when deletion is successful', async () => { const id = 'test-id'; const name = 'sample monitor'; + const store = createRealStore(); + render( ', () => { }, ] as unknown as MonitorManagementListResult['monitors'] } - /> + />, + { store } ); - userEvent.click(screen.getByTestId('monitorManagementDeleteMonitor')); + fireEvent.click(screen.getByRole('button')); - expect(onUpdate).toHaveBeenCalled(); - }); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); - it('shows loading spinner while waiting for monitor to delete', () => { - const id = 'test-id'; - useFetcher.mockReturnValue({ - data: {}, - status: FETCH_STATUS.LOADING, - refetch: () => {}, + await waitFor(() => { + expect(onUpdate).toHaveBeenCalled(); }); - render( - - ); - expect(screen.getByLabelText('Deleting monitor...')).toBeInTheDocument(); + expect(store.getState().deleteMonitor.deletedMonitorIds.includes(id)).toEqual(true); }); }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx index ebec71c14c477..d82613d7a2ab0 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx @@ -7,22 +7,19 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; -import { - EuiButtonIcon, - EuiCallOut, - EuiConfirmModal, - EuiLoadingSpinner, - EuiSpacer, -} from '@elastic/eui'; - -import { FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { EuiButtonIcon, EuiCallOut, EuiConfirmModal, EuiSpacer } from '@elastic/eui'; + +import { useDispatch, useSelector } from 'react-redux'; +import { deleteMonitorAction } from '../../../state/actions/delete_monitor'; +import { AppState } from '../../../state'; import { ProjectMonitorDisclaimer, PROJECT_MONITOR_TITLE, } from '../../../../apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor'; -import { deleteMonitor } from '../../../state/api'; -import { kibanaService } from '../../../state/kibana_service'; +import { + deleteMonitorLoadingSelector, + deleteMonitorSuccessSelector, +} from '../../../state/selectors'; export const DeleteMonitor = ({ configId, @@ -37,61 +34,34 @@ export const DeleteMonitor = ({ isProjectMonitor?: boolean; onUpdate: () => void; }) => { - const [isDeleting, setIsDeleting] = useState(false); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); + const isDeleting = useSelector((state: AppState) => + deleteMonitorLoadingSelector(state, configId) + ); + + const isSuccessfullyDeleted = useSelector((state: AppState) => + deleteMonitorSuccessSelector(state, configId) + ); + + const dispatch = useDispatch(); + const onConfirmDelete = () => { - setIsDeleting(true); + dispatch(deleteMonitorAction.get({ id: configId, name })); setIsDeleteModalVisible(false); }; - const showDeleteModal = () => setIsDeleteModalVisible(true); - const { status } = useFetcher(() => { - if (isDeleting) { - return deleteMonitor({ id: configId }); - } - }, [configId, isDeleting]); + const showDeleteModal = () => setIsDeleteModalVisible(true); const handleDelete = () => { showDeleteModal(); }; useEffect(() => { - if (!isDeleting) { - return; - } - if (status === FETCH_STATUS.SUCCESS || status === FETCH_STATUS.FAILURE) { - setIsDeleting(false); - } - if (status === FETCH_STATUS.FAILURE) { - kibanaService.toasts.addDanger( - { - title: toMountPoint( -

{MONITOR_DELETE_FAILURE_LABEL}

- ), - }, - { toastLifeTimeMs: 3000 } - ); - } else if (status === FETCH_STATUS.SUCCESS) { + if (isSuccessfullyDeleted) { onUpdate(); - kibanaService.toasts.addSuccess( - { - title: toMountPoint( -

- {i18n.translate( - 'xpack.synthetics.monitorManagement.monitorDeleteSuccessMessage.name', - { - defaultMessage: 'Deleted "{name}"', - values: { name }, - } - )} -

- ), - }, - { toastLifeTimeMs: 3000 } - ); } - }, [setIsDeleting, onUpdate, status, name, isDeleting]); + }, [onUpdate, isSuccessfullyDeleted]); const destroyModal = ( - {status === FETCH_STATUS.LOADING ? ( - - ) : ( - - )} + + {isDeleteModalVisible && destroyModal} ); @@ -151,18 +119,3 @@ const DELETE_MONITOR_LABEL = i18n.translate( defaultMessage: 'Delete monitor', } ); - -// TODO: Discuss error states with product -const MONITOR_DELETE_FAILURE_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.monitorDeleteFailureMessage', - { - defaultMessage: 'Monitor was unable to be deleted. Please try again later.', - } -); - -const MONITOR_DELETE_LOADING_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.monitorDeleteLoadingMessage', - { - defaultMessage: 'Deleting monitor...', - } -); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx index 7e475a0c7116a..93fea9ca0acc3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx @@ -180,7 +180,6 @@ export const MonitorManagementList = ({ }), sortable: true, render: (urls: string, { hosts }: TCPSimpleFields | ICMPSimpleFields) => urls || hosts, - truncateText: true, textOnly: true, }, { @@ -205,6 +204,7 @@ export const MonitorManagementList = ({ }), render: (fields: EncryptedSyntheticsMonitorWithId) => ( { +export const createRealStore = (): Store => { const sagaMW = createSagaMiddleware(); const store = createReduxStore(rootReducer, applyMiddleware(sagaMW)); sagaMW.run(rootEffect); return store; }; -export const MountWithReduxProvider: React.FC<{ state?: AppState; useRealStore?: boolean }> = ({ - children, - state, - useRealStore, -}) => { - const store = useRealStore +export const MountWithReduxProvider: React.FC<{ + state?: AppState; + useRealStore?: boolean; + store?: Store; +}> = ({ children, state, store, useRealStore }) => { + const newStore = useRealStore ? createRealStore() : { dispatch: jest.fn(), @@ -38,5 +38,5 @@ export const MountWithReduxProvider: React.FC<{ state?: AppState; useRealStore?: [Symbol.observable]: jest.fn(), }; - return {children}; + return {children}; }; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx index 4435ab081192b..e1bee1d621f12 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx @@ -28,6 +28,7 @@ import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { Store } from 'redux'; import { mockState } from '../__mocks__/uptime_store.mock'; import { MountWithReduxProvider } from './helper_with_redux'; import { AppState } from '../../state'; @@ -221,12 +222,17 @@ export function WrappedHelper({ url, useRealStore, path, + store, history = createMemoryHistory(), -}: RenderRouterOptions & { children: ReactElement; useRealStore?: boolean }) { +}: RenderRouterOptions & { + children: ReactElement; + useRealStore?: boolean; + store?: Store; +}) { const testState: AppState = merge({}, mockState, state); return ( - + {children} @@ -246,7 +252,8 @@ export function render( url, path, useRealStore, - }: RenderRouterOptions & { useRealStore?: boolean } = {} + store, + }: RenderRouterOptions & { useRealStore?: boolean; store?: Store } = {} ): any { if (url) { history = getHistoryFromUrl(url); @@ -262,6 +269,7 @@ export function render( state={state} path={path} useRealStore={useRealStore} + store={store} > {ui} , diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/delete_monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/delete_monitor.ts new file mode 100644 index 0000000000000..194b02cd16038 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/delete_monitor.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createAsyncAction } from './utils'; + +export const deleteMonitorAction = createAsyncAction< + { id: string; name: string }, + string, + { id: string; error: Error } +>('DELETE_MONITOR'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts index 5ddc90be61f23..739cadb2d65b9 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts @@ -10,13 +10,13 @@ import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import type { UptimeAlertTypeParams } from '../alerts/alerts'; -export interface AsyncAction { +export interface AsyncAction { get: (payload: Payload) => Action; success: (payload: SuccessPayload) => Action; - fail: (payload: IHttpFetchError) => Action; + fail: (payload: ErrorPayload) => Action; } -export interface AsyncActionOptionalPayload - extends AsyncAction { +export interface AsyncActionOptionalPayload + extends AsyncAction { get: (payload?: Payload) => Action; } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts index 6e8f15ec3780f..cd930061459f8 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts @@ -9,15 +9,15 @@ import { createAction } from 'redux-actions'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { AsyncAction, AsyncActionOptionalPayload } from './types'; -export function createAsyncAction( +export function createAsyncAction( actionStr: string -): AsyncActionOptionalPayload; -export function createAsyncAction( +): AsyncActionOptionalPayload; +export function createAsyncAction( actionStr: string -): AsyncAction { +): AsyncAction { return { get: createAction(actionStr), success: createAction(`${actionStr}_SUCCESS`), - fail: createAction(`${actionStr}_FAIL`), + fail: createAction(`${actionStr}_FAIL`), }; } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/delete_monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/delete_monitor.tsx new file mode 100644 index 0000000000000..fc4a68c974620 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/delete_monitor.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { put, call, takeEvery } from 'redux-saga/effects'; +import { Action } from 'redux-actions'; +import { i18n } from '@kbn/i18n'; +import { deleteMonitorAction } from '../actions/delete_monitor'; +import { deleteMonitor } from '../api'; +import { kibanaService } from '../kibana_service'; + +export function* deleteMonitorEffect() { + yield takeEvery( + String(deleteMonitorAction.get), + function* (action: Action<{ id: string; name: string }>) { + try { + const { id, name } = action.payload; + yield call(deleteMonitor, { id }); + yield put(deleteMonitorAction.success(id)); + kibanaService.core.notifications.toasts.addSuccess({ + title: toMountPoint( +

+ {i18n.translate( + 'xpack.synthetics.monitorManagement.monitorDeleteSuccessMessage.name', + { + defaultMessage: 'Deleted "{name}"', + values: { name }, + } + )} +

+ ), + }); + } catch (err) { + kibanaService.core.notifications.toasts.addError(err, { + title: MONITOR_DELETE_FAILURE_LABEL, + }); + yield put(deleteMonitorAction.fail({ id: action.payload.id, error: err })); + } + } + ); +} + +const MONITOR_DELETE_FAILURE_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.monitorDeleteFailureMessage', + { + defaultMessage: 'Monitor was unable to be deleted. Please try again later.', + } +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts index 07df2a909d4b8..3fade3e59257e 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts @@ -6,6 +6,7 @@ */ import { fork } from 'redux-saga/effects'; +import { deleteMonitorEffect } from './delete_monitor'; import { fetchAgentPoliciesEffect } from '../private_locations'; import { fetchMonitorDetailsEffect } from './monitor'; import { fetchMonitorListEffect, fetchUpdatedMonitorEffect } from './monitor_list'; @@ -51,4 +52,5 @@ export function* rootEffect() { yield fork(pruneBlockCache); yield fork(fetchSyntheticsServiceAllowedEffect); yield fork(fetchAgentPoliciesEffect); + yield fork(deleteMonitorEffect); } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/delete_monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/delete_monitor.ts new file mode 100644 index 0000000000000..b28cc214f0e8b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/delete_monitor.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createReducer, PayloadAction } from '@reduxjs/toolkit'; +import { WritableDraft } from 'immer/dist/types/types-external'; +import { deleteMonitorAction } from '../actions/delete_monitor'; + +export interface DeleteMonitorState { + error?: Record; + loading?: string[]; + deletedMonitorIds?: string[]; +} + +export const initialState: DeleteMonitorState = { + error: {}, + loading: [], + deletedMonitorIds: [], +}; + +export const deleteMonitorReducer = createReducer(initialState, (builder) => { + builder + .addCase( + String(deleteMonitorAction.get), + ( + state: WritableDraft, + action: PayloadAction<{ id: string; name: string }> + ) => ({ + ...state, + loading: [...(state.loading ?? []), action.payload.id], + error: { ...state.error, [action.payload.id]: undefined }, + }) + ) + .addCase( + String(deleteMonitorAction.success), + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: state.loading?.filter((id) => id !== action.payload), + deletedMonitorIds: [...(state.deletedMonitorIds ?? []), action.payload], + }) + ) + .addCase( + String(deleteMonitorAction.fail), + ( + state: WritableDraft, + action: PayloadAction<{ id: string; error: Error }> + ) => ({ + ...state, + loading: state.loading?.filter((id) => id !== action.payload.id), + error: { ...state.error, [action.payload.id]: action.payload.error }, + }) + ); +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts index dc4db2d58ac61..554d8f1a44cd8 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts @@ -6,6 +6,7 @@ */ import { combineReducers } from 'redux'; +import { deleteMonitorReducer, DeleteMonitorState } from './delete_monitor'; import { agentPoliciesReducer, AgentPoliciesState } from '../private_locations'; import { monitorReducer, MonitorState } from './monitor'; import { uiReducer, UiState } from './ui'; @@ -47,6 +48,7 @@ export interface RootState { synthetics: SyntheticsReducerState; testNowRuns: TestNowRunsState; agentPolicies: AgentPoliciesState; + deleteMonitor: DeleteMonitorState; } export const rootReducer = combineReducers({ @@ -69,4 +71,5 @@ export const rootReducer = combineReducers({ synthetics: syntheticsReducer, testNowRuns: testNowRunsReducer, agentPolicies: agentPoliciesReducer, + deleteMonitor: deleteMonitorReducer, }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts index d250eb8bcf059..8a91c017635ae 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts @@ -19,6 +19,15 @@ export const monitorDetailsSelector = (state: AppState, summary: any) => { return state.monitor.monitorDetailsList[summary.monitor_id]; }; +export const deleteMonitorLoadingSelector = (state: AppState, id?: string) => { + if (!id) return (state.deleteMonitor.loading ?? []).length > 0; + return state.deleteMonitor.loading?.includes(id) ?? false; +}; + +export const deleteMonitorSuccessSelector = (state: AppState, id: string) => { + return state.deleteMonitor.deletedMonitorIds?.includes(id) ?? false; +}; + export const monitorDetailsLoadingSelector = (state: AppState) => state.monitor.loading; export const monitorLocationsSelector = (state: AppState, monitorId: string) => { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index b2746c0c2fd82..275268ecc2883 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -31082,7 +31082,6 @@ "xpack.synthetics.monitorManagement.monitorAdvancedOptions.monitorNamespaceFieldLabel": "Espace de nom", "xpack.synthetics.monitorManagement.monitorAdvancedOptions.namespaceHelpLearnMoreLabel": "En savoir plus", "xpack.synthetics.monitorManagement.monitorDeleteFailureMessage": "Impossible de supprimer le moniteur. Réessayez plus tard.", - "xpack.synthetics.monitorManagement.monitorDeleteLoadingMessage": "Suppression du moniteur...", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "Moniteur mis à jour.", "xpack.synthetics.monitorManagement.monitorFailureMessage": "Impossible d'enregistrer le moniteur. Réessayez plus tard.", "xpack.synthetics.monitorManagement.monitorList.actions": "Actions", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 245b2a33195fe..58bd193a4ba78 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -31058,7 +31058,6 @@ "xpack.synthetics.monitorManagement.monitorAdvancedOptions.monitorNamespaceFieldLabel": "名前空間", "xpack.synthetics.monitorManagement.monitorAdvancedOptions.namespaceHelpLearnMoreLabel": "詳細情報", "xpack.synthetics.monitorManagement.monitorDeleteFailureMessage": "モニターを削除できませんでした。しばらくたってから再試行してください。", - "xpack.synthetics.monitorManagement.monitorDeleteLoadingMessage": "モニターを削除しています...", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "モニターは正常に更新されました。", "xpack.synthetics.monitorManagement.monitorFailureMessage": "モニターを保存できませんでした。しばらくたってから再試行してください。", "xpack.synthetics.monitorManagement.monitorList.actions": "アクション", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 87b560ac76f5c..edb5ad718e653 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -31093,7 +31093,6 @@ "xpack.synthetics.monitorManagement.monitorAdvancedOptions.monitorNamespaceFieldLabel": "命名空间", "xpack.synthetics.monitorManagement.monitorAdvancedOptions.namespaceHelpLearnMoreLabel": "了解详情", "xpack.synthetics.monitorManagement.monitorDeleteFailureMessage": "无法删除监测。请稍后重试。", - "xpack.synthetics.monitorManagement.monitorDeleteLoadingMessage": "正在删除监测......", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "已成功更新监测。", "xpack.synthetics.monitorManagement.monitorFailureMessage": "无法保存监测。请稍后重试。", "xpack.synthetics.monitorManagement.monitorList.actions": "操作",